[Feature]add MT2731_MP2_MR2_SVN388 baseline version

Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/bsp/lk/platform/mediatek/mt2712/bl2_bl33_options.mk b/src/bsp/lk/platform/mediatek/mt2712/bl2_bl33_options.mk
new file mode 100644
index 0000000..dac1fe8
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/bl2_bl33_options.mk
@@ -0,0 +1,103 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+# SCP config
+ENABLE_SCP_LOAD ?= 0
+ENABLE_SCP_AUX_LOAD ?= 0
+
+ifeq ($(MTK_TINYSYS_SCP_SUPPORT),1)
+ENABLE_SCP_LOAD := 1
+ENABLE_SCP_AUX_LOAD := 1
+endif
+
+ifeq ($(MTK_TINYSYS_SCP_FUNCTION_SAFETY),1)
+ENABLE_SCP_LOAD := 1
+endif
+
+ifeq ($(LK_AS_BL33),1)
+# LK build as BL33
+
+# memory setting
+MEMBASE ?= 0x73500000
+KERNEL_LOAD_OFFSET ?= 0x0
+MEMSIZE ?= 0x100000 # 1MB
+
+ifeq ($(WITH_KERNEL_VM),1)
+KERNEL_BASE ?= 0xfffffff073500000   # KERNEL_ASPACE_BASE + MEMBASE
+else
+KERNEL_BASE ?= 0x73500000
+endif
+
+# image load options
+ENABLE_TZ_LOAD := 0                 # bl33 doesnt' load tz
+ENABLE_BL33_LOAD := 0               # bl33 doesn't load itself
+ENABLE_KERNEL_LOAD := 1             # bl33 loads kernel and dtbo
+ENABLE_SLAVE_CPU_LOAD ?= 0          # bl33 doesn't load slave cpu by default
+
+# bl33 boot options
+BL33_BOOT_NEXT_64BITS ?= 1          # boot stage after bl33 is 64 bits
+
+# fastboot mode option
+OPTION_CLEAR_FASTBOOT_FLAG ?= 1
+
+# recovery mode option
+OPTION_CLEAR_RECOVERY_FLAG ?= 1
+
+MODULE_SRCS += $(LOCAL_DIR)/platform_bl33.c
+
+GLOBAL_DEFINES += \
+    BL33_BOOT_NEXT_64BITS=$(BL33_BOOT_NEXT_64BITS)
+
+else
+
+# LK build as BL2
+
+# memory setting
+MEMBASE ?= 0x200000
+KERNEL_LOAD_OFFSET ?= 0x1000
+MEMSIZE ?= 0x40000 # 256KB
+
+ifeq ($(WITH_KERNEL_VM),1)
+KERNEL_BASE ?= 0xfffffff000200000   # KERNEL_ASPACE_BASE + MEMBASE
+else
+KERNEL_BASE ?= 0x200000             # KERNEL_ASPACE_BASE + MEMBASE
+endif
+
+# dram calibration setting
+DRAM_SZ ?= DRAM_SZ_AUTO             # DRAM_SZ_2G, DRAM_SZ_3G, DRAM_SZ_AUTO
+DRAM_BW ?= DRAM_BW_AUTO             # DRAM_BW_32BIT, DRAM_BW_64BIT, DRAM_BW_AUTO
+
+# image load options
+ENABLE_TZ_LOAD := 1                 # loads tz
+ENABLE_BL33_LOAD := 1
+ENABLE_BUILTIN_BL33 ?= 1            # default use builtin 'do-nothing-but-jump' bl33
+ifeq ($(ENABLE_BUILTIN_BL33),1)
+ENABLE_KERNEL_LOAD := 1             # if builtin bl33, bl2 also loads kernel (& dtbo)
+OPTION_CLEAR_FASTBOOT_FLAG ?= 1
+OPTION_CLEAR_RECOVERY_FLAG ?= 1
+else
+ENABLE_KERNEL_LOAD := 0
+OPTION_CLEAR_FASTBOOT_FLAG ?= 0
+OPTION_CLEAR_RECOVERY_FLAG ?= 0
+endif
+ENABLE_SLAVE_CPU_LOAD ?= 0          # bl2 doesn't load slave cpu image
+
+MODULE_SRCS += $(LOCAL_DIR)/platform_bl2.c
+
+GLOBAL_DEFINES += \
+    $(DRAM_SZ) \
+    $(DRAM_BW) \
+    ENABLE_BUILTIN_BL33=$(ENABLE_BUILTIN_BL33)
+
+endif
+
+GLOBAL_DEFINES += \
+    MEMBASE=$(MEMBASE) \
+    MEMSIZE=$(MEMSIZE) \
+    ENABLE_TZ_LOAD=$(ENABLE_TZ_LOAD) \
+    ENABLE_BL33_LOAD=$(ENABLE_BL33_LOAD) \
+    ENABLE_KERNEL_LOAD=$(ENABLE_KERNEL_LOAD) \
+    OPTION_CLEAR_FASTBOOT_FLAG=$(OPTION_CLEAR_FASTBOOT_FLAG) \
+    OPTION_CLEAR_RECOVERY_FLAG=$(OPTION_CLEAR_RECOVERY_FLAG) \
+    ENABLE_SLAVE_CPU_LOAD=$(ENABLE_SLAVE_CPU_LOAD) \
+    ENABLE_SCP_LOAD=$(ENABLE_SCP_LOAD) \
+    ENABLE_SCP_AUX_LOAD=$(ENABLE_SCP_AUX_LOAD)
diff --git a/src/bsp/lk/platform/mediatek/mt2712/boot_mode.c b/src/bsp/lk/platform/mediatek/mt2712/boot_mode.c
new file mode 100644
index 0000000..ad8731a
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/boot_mode.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2020 MediaTek Inc.
+ *
+ * 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
+ */
+#if BCB_RECOVERY_SUPPORT
+#include <boot_mode.h>
+#include <lib/bio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <platform/mtk_wdt.h>
+
+/*
+ * BCB = bootloader control block
+ * Offset 0 ~ 2KB of BCB_PARTITION stored the BCB message struct
+ */
+#define BCB_PARTITION   "misc"
+
+struct bcb_msg {
+    char command[32];
+    char status[32];
+    char recovery[1024];
+    char reserved[960];
+};
+
+static const char cmd_recovery[] = "boot-recovery";
+static const char cmd_fastboot[] = "boot-fastboot";
+
+static bool check_bcb_recovery_mode(void)
+{
+    bdev_t *dev;
+    ssize_t bytes_read;
+    struct bcb_msg *msg;
+    bool ret = false;
+
+    dev = bio_open_by_label(BCB_PARTITION);
+    if (!dev)
+        goto err_exit;
+
+    msg = (struct bcb_msg *)malloc(sizeof(struct bcb_msg));
+    if (!msg)
+        goto err_close_dev;
+
+    bytes_read = bio_read(dev, msg, 0, sizeof(struct bcb_msg));
+    if ((size_t)bytes_read < sizeof(struct bcb_msg))
+        goto err_free_mem;
+
+    if ((strncmp(msg->command, cmd_recovery, strlen(cmd_recovery)) == 0) ||
+        (strncmp(msg->command, cmd_fastboot, strlen(cmd_fastboot)) == 0))
+        ret = true;
+
+err_free_mem:
+    free(msg);
+    msg = NULL;
+
+err_close_dev:
+    bio_close(dev);
+
+err_exit:
+    return ret;
+}
+
+static bool write_bcb_fastbootd_mode(void)
+{
+    bdev_t *dev;
+    ssize_t bytes_read;
+    ssize_t bytes_write;
+    ssize_t erase_size;
+    struct bcb_msg *msg;
+    bool ret = false;
+    int ret_erase;
+
+    dev = bio_open_by_label(BCB_PARTITION);
+    if (!dev)
+        goto err_exit;
+
+    ASSERT(dev->geometry != NULL);
+    erase_size = (size_t)dev->geometry->erase_size;
+
+    msg = (struct bcb_msg *)malloc(erase_size);
+    if (!msg)
+        goto err_close_dev;
+
+    bytes_read = bio_read(dev, msg, 0, erase_size);
+    if ((size_t)bytes_read < erase_size)
+        goto err_free_mem;
+
+    memset(msg->command, 0, strlen(cmd_fastboot) + 1);
+    strncpy(msg->command, cmd_fastboot, strlen(cmd_fastboot));
+
+    ret_erase = bio_erase(dev, 0, erase_size);
+    if (ret_erase < 0)
+        goto err_free_mem;
+
+    bytes_write = bio_write(dev, msg, 0, erase_size);
+    if ((size_t)bytes_write < erase_size)
+        goto err_free_mem;
+    ret = true;
+
+err_free_mem:
+    free(msg);
+    msg = NULL;
+
+err_close_dev:
+    bio_close(dev);
+
+err_exit:
+    return ret;
+}
+
+uint32_t plat_get_boot_mode(void)
+{
+    if (check_fastbootd_mode())
+    {
+        set_clr_fastbootd_mode(false);
+        write_bcb_fastbootd_mode();
+    }
+    return check_bcb_recovery_mode() ? RECOVERY_BOOT : NORMAL_BOOT;
+}
+#endif
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/audio/audio_clk_enable.c b/src/bsp/lk/platform/mediatek/mt2712/drivers/audio/audio_clk_enable.c
new file mode 100644
index 0000000..6634bdd
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/audio/audio_clk_enable.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <reg.h>
+#include <platform/audio_clk_enable.h>
+#include <platform/pll.h>
+#include <trace.h>
+
+#define LOCAL_TRACE 0
+
+#ifndef LK_ENABLE_AUDIO_CLK
+
+void mt_audio_clk_enable(void)
+{
+    return;
+}
+
+#else
+
+static uintptr_t g_afe_enable_reg[] = {
+    ASYS_TOP_CON,
+    AFE_DAC_CON0
+};
+
+static uint32_t g_afe_enable_val[] = {
+    0x00000003U,
+    0x00000001U,
+};
+
+static uintptr_t g_clk_enable_reg[] = {
+    AUDIO_TOP_CON4,
+};
+
+static uint32_t g_clk_enable_val[] = {
+#if LK_ENABLE_I2S_SEP_CLK
+    0xff1fffbfU,
+#elif LK_ENABLE_I2S_COCLK
+    0xff1fffbeU,
+#else
+    0xff1fffffU,
+#endif
+};
+
+#if ENABLE_TDM_CLK
+#if LK_ENABLE_TDM_SEP_CLK
+static uintptr_t g_tdm_enable_reg[] = {
+    AUDIO_TOP_CON1,
+    AUDIO_TOP_CON2,
+    AFE_TDM_G1_CON1,
+    AFE_TDM_G1_CON2,
+    AFE_TDM_G1_CON1
+};
+static uint32_t g_tdm_enable_val[] = {
+    0x0f000000U,
+    0x00000f02U,
+    0x0fbfab1aU,
+    0xa500ff40U,
+    0x0fbfab1bU
+};
+#elif LK_ENABLE_TDM_COCLK
+static uintptr_t g_tdm_enable_reg[] = {
+    AUDIO_TOP_CON1,
+    AUDIO_TOP_CON2,
+    AFE_TDM_G1_CON1,
+    AFE_TDM_G1_CON2,
+    AFE_TDM_IN_CON1,
+    AFE_TDM_IN_CON2,
+    AFE_TDM_G1_CON1,
+    AFE_TDM_IN_CON1
+};
+static uint32_t g_tdm_enable_val[] = {
+    0x0f000000U,
+    0x00000f02U,
+    0x0fbfab0aU,
+    0xa500ff40U,
+    0x0fa6230aU,
+    0x00000000U,
+    0x0fbfab0bU,
+    0x0fa6230bU
+};
+#endif
+#endif
+
+#if ENABLE_I2S_CLK
+#if LK_ENABLE_I2S_SEP_CLK
+static uintptr_t g_i2s_enable_reg[] = {
+    ASYS_I2SO1_CON,
+    ASMO_TIMING_CON1,
+    ASYS_I2SO1_CON
+};
+
+static uint32_t g_i2s_enable_val[] = {
+    0x0000050aU,
+    0x00000005U,
+    0x0000050bU
+};
+#elif LK_ENABLE_I2S_COCLK
+static uintptr_t g_i2s_enable_reg[] = {
+    ASYS_I2SO1_CON,
+    ASMO_TIMING_CON1,
+    ASYS_I2SIN1_CON,
+    ASMI_TIMING_CON1,
+    ASYS_I2SO1_CON,
+    ASYS_I2SIN1_CON
+};
+
+static uint32_t g_i2s_enable_val[] = {
+    0x0000050aU,
+    0x00000005U,
+    0x8000850aU,
+    0x00000005U,
+    0x0000050bU,
+    0x8000850bU,
+};
+#endif
+#endif
+
+#ifdef LK_AUDIO_USE_EXT_CLK
+static uintptr_t g_set_apll_src_reg[] = {
+    PLL_TEST_CON0,
+    AP_PLL_CON5,
+    APLL1_CON0,
+    APLL1_CON1,
+    APLL1_CON2,
+    APLL1_CON1,
+    APLL2_CON0,
+    APLL2_CON1,
+    APLL2_CON2,
+    APLL2_CON1,
+    AP_PLL_CON5,
+};
+
+static uint32_t g_set_apll_src_val[] = {
+    0x00060000U,
+    0x00000000U,
+    0x00000030U,
+    0x40000000U,
+    0x40000001U,
+    0x80000000U,
+    0x00000030U,
+    0x3ACCCCCCU,
+    0x3ACCCCCDU,
+    0x80000000U,
+    0x00000003U,
+};
+
+static uint32_t g_set_apll_src_mask[] = {
+    0x00060000U,
+    0x00000003U,
+    0x00000070U,
+    0xFFFFFFFFU,
+    0xFFFFFFFFU,
+    0x80000000U,
+    0x00000070U,
+    0xFFFFFFFFU,
+    0xFFFFFFFFU,
+    0x80000000U,
+    0x00000003U,
+};
+#endif
+
+void audio_set_reg_addr_val_mask(uintptr_t addr, uint32_t val, uint32_t mask)
+{
+    volatile uint32_t val_orig = audio_get_reg(addr);
+    volatile uint32_t val_to_write = (val_orig & (~mask)) | (val & mask);
+
+    audio_set_reg_addr_val(addr, val_to_write);
+}
+
+void write_reg_seq(uintptr_t *reg_arr, uint32_t *val_arr, size_t size)
+{
+    size_t i;
+    for (i=0; i<size; ++i) {
+        audio_set_reg_addr_val(reg_arr[i], val_arr[i]);
+    }
+}
+
+void write_reg_seq_with_mask(uintptr_t *reg_arr, uint32_t *val_arr,
+                             uint32_t *mask_arr, size_t size)
+{
+    size_t i;
+    for (i=0; i<size; ++i) {
+        audio_set_reg_addr_val_mask(reg_arr[i], val_arr[i], mask_arr[i]);
+    }
+}
+
+void mt_audio_clk_enable(void)
+{
+
+    LTRACEF("open clk start time tag %u\n",
+            audio_get_reg(IO_PHYS + 0x0000c008));
+
+#ifdef LK_AUDIO_USE_EXT_CLK
+    /* Set gpio here (GPIO186 set to mode 1) for EXT_CLK 1  */
+    LTRACEF("Set gpio (GPIO186 set to mode 1) for EXT_CLK 1\n");
+    audio_set_reg_addr_val_mask(IO_PHYS + 0x00005750, 0x00000008, 0x00000038);
+
+    /* Set rate & external source for apll1/apll2 */
+    write_reg_seq_with_mask(g_set_apll_src_reg,
+        g_set_apll_src_val, g_set_apll_src_mask,
+        sizeof(g_set_apll_src_reg) / sizeof(g_set_apll_src_reg[0]));
+
+#endif
+
+    /* set ccf clk to right value which is not the same with default */
+    audio_set_reg_addr_val_mask(CLK_CFG_12, 0x01000001, 0x03000003);
+    audio_set_reg_addr_val_mask(CLK_AUDDIV_1, 0x0F00003F, 0xFF0000FF);
+    audio_set_reg_addr_val_mask(CLK_CFG_10, 0x03000000, 0x07000000);
+    audio_set_reg_addr_val_mask(CLK_CFG_11, 0x00000003, 0x00000007);
+
+    /* enable clks in ccf */
+    audio_set_reg_addr_val_mask(CLK_CFG_12, 0x0, 0x80000008);
+    audio_set_reg_addr_val_mask(CLK_CFG_10, 0x0, 0x80808000);
+    audio_set_reg_addr_val_mask(CLK_CFG_11, 0x0, 0x00000080);
+    audio_set_reg_addr_val_mask(CLK_CFG_4, 0x0, 0x80000000);
+
+#if ENABLE_TDM_CLK
+    /* Set gpio here (GPIO200~GPIO203 set to mode 1) for TDM out */
+    LTRACEF("Set gpio (GPIO200~GPIO203 set to mode 1) for TDM out\n");
+    audio_set_reg_addr_val_mask(IO_PHYS + 0x00005780, 0x00000249, 0x00000FFF);
+#endif
+
+#if ENABLE_I2S_CLK
+    /* Set gpio here (GPIO192~GPIO195 set to mode 1) for I2S out */
+    LTRACEF("Set gpio (GPIO192~GPIO195 set to mode 1) for I2S out\n");
+    audio_set_reg_addr_val_mask(IO_PHYS + 0x00005760, 0x00001240, 0x00007FC0);
+    audio_set_reg_addr_val_mask(IO_PHYS + 0x00005770, 0x00000001, 0x00000007);
+#endif
+
+    write_reg_seq(g_afe_enable_reg, g_afe_enable_val,
+                  sizeof(g_afe_enable_reg) / sizeof(g_afe_enable_reg[0]));
+    write_reg_seq(g_clk_enable_reg, g_clk_enable_val,
+                  sizeof(g_clk_enable_reg) / sizeof(g_clk_enable_reg[0]));
+#if ENABLE_TDM_CLK
+    write_reg_seq(g_tdm_enable_reg, g_tdm_enable_val,
+                  sizeof(g_tdm_enable_reg) / sizeof(g_tdm_enable_reg[0]));
+#endif
+
+#if ENABLE_I2S_CLK
+    write_reg_seq(g_i2s_enable_reg, g_i2s_enable_val,
+                  sizeof(g_i2s_enable_reg) / sizeof(g_i2s_enable_reg[0]));
+#endif
+    LTRACEF("open clk end time tag %u\n",
+            audio_get_reg(IO_PHYS + 0x0000c008));
+}
+
+#endif //LK_ENABLE_AUDIO_CLK
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/audio/include/platform/audio_clk_enable.h b/src/bsp/lk/platform/mediatek/mt2712/drivers/audio/include/platform/audio_clk_enable.h
new file mode 100644
index 0000000..27b3ab8
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/audio/include/platform/audio_clk_enable.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once
+
+#include <platform/mt_reg_base.h>
+#define AFE_REG_BASE (IO_PHYS + 0x01220000)
+
+#define AUDIO_TOP_CON0 (AFE_REG_BASE + 0x0000U)
+#define AUDIO_TOP_CON1 (AFE_REG_BASE + 0x0004U)
+#define AUDIO_TOP_CON2 (AFE_REG_BASE + 0x0008U)
+#define AUDIO_TOP_CON4 (AFE_REG_BASE + 0x0010U)
+#define ASMI_TIMING_CON1 (AFE_REG_BASE + 0x0100U)
+#define ASMO_TIMING_CON1 (AFE_REG_BASE + 0x0104U)
+#define AFE_TDM_G1_CON1 (AFE_REG_BASE + 0x0290U)
+#define AFE_TDM_G1_CON2 (AFE_REG_BASE + 0x0294U)
+#define AFE_TDM_G2_CON1 (AFE_REG_BASE + 0x02a0U)
+#define AFE_TDM_G2_CON2 (AFE_REG_BASE + 0x02a4U)
+#define AFE_TDM_IN_CON1 (AFE_REG_BASE + 0x02b8U)
+#define AFE_TDM_IN_CON2 (AFE_REG_BASE + 0x02bcU)
+#define ASYS_TOP_CON (AFE_REG_BASE + 0x0600U)
+#define ASYS_I2SIN1_CON (AFE_REG_BASE + 0x0604U)
+#define ASYS_I2SO1_CON (AFE_REG_BASE + 0x061cU)
+#define AFE_DAC_CON0 (AFE_REG_BASE + 0x1200U)
+
+#define AFE_SGEN_CON0 (AFE_REG_BASE + 0x01f0U)
+
+#define audio_get_reg(addr) readl(addr)
+#define audio_set_reg_addr_val(addr, val) writel(val, addr)
+
+#define ENABLE_TDM_CLK (LK_ENABLE_TDM_SEP_CLK || LK_ENABLE_TDM_COCLK)
+#define ENABLE_I2S_CLK (LK_ENABLE_I2S_SEP_CLK || LK_ENABLE_I2S_COCLK)
+
+void mt_audio_clk_enable(void);
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/audio/rules.mk b/src/bsp/lk/platform/mediatek/mt2712/drivers/audio/rules.mk
new file mode 100644
index 0000000..1b915ea
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/audio/rules.mk
@@ -0,0 +1,43 @@
+# define build parameters to enable "lk open clock feature"
+# you may define the parameters in project/XXX.mk for XXX project
+# ENABLE_AUDIO_CLK := 1 -> enable the feature
+# AUDIO_USE_EXT_CLK := 1 -> apll1/apll2 use external clk
+# ENABLE_TDM_CLK_MODE := 1 -> enable the TDM out 0 with non-coclk setting
+# ENABLE_TDM_CLK_MODE := 2 -> enable the TDM out 0 with coclk setting
+# ENABLE_I2S_CLK_MODE := 1 -> enable the I2S out 0 with non-coclk setting
+# ENABLE_I2S_CLK_MODE := 2 -> enable the I2S out 0 with coclk setting
+#
+# Note: The TDM/I2S out format will be fixed once you enable the feature.
+# If you want to change the setting there is some parameter you need to change:
+#    TDM Sample rate: AUDIO_TOP_CON1 bit 31:24, AUDIO_TOP_CON2 bit 15:8
+#    TDM data/clk format: AFE_TDM_G1_CON1, AFE_TDM_IN_CON1
+#    I2S Sample rate: ASMO_TIMING_CON1, ASMI_TIMING_CON1
+#    I2S format: ASYS_I2SO1_CON, ASYS_I2SIN1_CON
+LOCAL_DIR := $(GET_LOCAL_DIR)
+MODULE := $(LOCAL_DIR)
+
+ifeq ($(strip $(ENABLE_AUDIO_CLK)),1)
+MODULE_DEFINES += LK_ENABLE_AUDIO_CLK
+
+ifeq ($(strip $(AUDIO_USE_EXT_CLK)),1)
+MODULE_DEFINES += LK_AUDIO_USE_EXT_CLK
+endif
+
+ifeq ($(strip $(ENABLE_TDM_CLK_MODE)),1)
+MODULE_DEFINES += LK_ENABLE_TDM_SEP_CLK=1
+else ifeq ($(strip $(ENABLE_TDM_CLK_MODE)),2)
+MODULE_DEFINES += LK_ENABLE_TDM_COCLK=1
+endif
+
+ifeq ($(strip $(ENABLE_I2S_CLK_MODE)),1)
+MODULE_DEFINES += LK_ENABLE_I2S_SEP_CLK=1
+else ifeq ($(strip $(ENABLE_I2S_CLK_MODE)),2)
+MODULE_DEFINES += LK_ENABLE_I2S_COCLK=1
+endif
+
+endif
+
+MODULE_SRCS += \
+    $(LOCAL_DIR)/audio_clk_enable.c \
+
+include make/module.mk
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/dcm/dcm.c b/src/bsp/lk/platform/mediatek/mt2712/drivers/dcm/dcm.c
new file mode 100644
index 0000000..c5d3b97
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/dcm/dcm.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2017 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <debug.h>
+#include <platform/dcm.h>
+#include <platform/mt_reg_base.h>
+#include <platform/reg_utils.h>
+
+#define BIT(bit) (1UL << (bit))
+
+#define INFRA_TOPCKGEN_DCMCTL	(INFRACFG_BASE	+ 0x0010)
+#define INFRA_TOPCKGEN_DCMDBC	(INFRACFG_BASE	+ 0x0014)
+#define INFRA_GLOBALCON_DCMCTL	(INFRACFG_BASE	+ 0x0050)
+#define INFRA_GLOBALCON_DCMDBC	(INFRACFG_BASE	+ 0x0054)
+#define INFRA_GLOBALCON_DCMFSEL	(INFRACFG_BASE	+ 0x0058)
+
+#define PERI_GLOBALCON_DCMCTL	(PERICFG_BASE	+ 0x0050)
+#define PERI_GLOBALCON_DCMDBC	(PERICFG_BASE	+ 0x0054)
+
+#define PERI_REV_REG            (PERICFG_BASE   + 0x0800)
+
+#define E1_WORKAROUND		1
+
+void mt_dcm_init(void)
+{
+	/* INFRA DCM */
+	setbits32(INFRA_TOPCKGEN_DCMCTL, BIT(0));
+
+#if E1_WORKAROUND
+	setbits32(INFRA_GLOBALCON_DCMCTL, BIT(0) | BIT(1) | BIT(9));
+	clrbits32(INFRA_GLOBALCON_DCMCTL, BIT(8));
+#else
+	setbits32(INFRA_GLOBALCON_DCMCTL, BIT(0) | BIT(1) | BIT(8) | BIT(9));
+#endif
+
+	setbits32(INFRA_GLOBALCON_DCMDBC, BIT(8) | BIT(24));
+
+	/* PERISYS GLOBALCON DCM */
+	setbits32(PERI_GLOBALCON_DCMCTL, BIT(0)| BIT(1));
+
+	/* PREVENT I2C TRANSFER RATE MAY SLOW DOWN
+         * DUE TO BUS CLOCK DCM FUNCTION*/
+	setbits32(PERI_REV_REG, BIT(8));
+
+}
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/efuse/mtk_devinfo.c b/src/bsp/lk/platform/mediatek/mt2712/drivers/efuse/mtk_devinfo.c
new file mode 100644
index 0000000..5299e57
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/efuse/mtk_devinfo.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 MediaTek Inc.
+ *
+ * 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 <platform/mtk_devinfo.h>
+
+int det_ca35_freq(void)
+{
+    int ca35_freq;
+
+    ca35_freq = get_ca35_freq();
+
+    if (ca35_freq == CA35_SPD_FREE) {
+#if defined(CA35_FREQ_806MHZ)
+        ca35_freq = CA35_SPD_806MHZ;
+#elif defined(CA35_FREQ_1001MHZ)
+        ca35_freq = CA35_SPD_1001MHZ;
+#else
+        ca35_freq = CA35_SPD_1196MHZ;
+#endif
+    }
+    return ca35_freq;
+}
+
+int det_ca72_freq(void)
+{
+    int ca72_freq;
+
+    ca72_freq = get_ca72_freq();
+
+    if (ca72_freq == CA72_SPD_FREE) {
+#if defined(CA72_FREQ_1001MHZ)
+        ca72_freq = CA72_SPD_1001MHZ;
+#elif defined(CA72_FREQ_1196MHZ)
+        ca72_freq = CA72_SPD_1196MHZ;
+#elif defined(CA72_FREQ_1391MHZ)
+        ca72_freq = CA72_SPD_1391MHZ;
+#elif defined(CA72_FREQ_1495MHZ)
+        ca72_freq = CA72_SPD_1495MHZ;
+#else
+        ca72_freq = CA72_SPD_1599MHZ;
+#endif
+    }
+    return ca72_freq;
+}
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/efuse/mtk_efuse.c b/src/bsp/lk/platform/mediatek/mt2712/drivers/efuse/mtk_efuse.c
new file mode 100644
index 0000000..dc89b83
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/efuse/mtk_efuse.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2017 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#include <debug.h>
+#include <mtk_i2c.h>
+#include <reg.h>
+#include <trace.h>
+
+#define LOCAL_TRACE 0
+
+static int enable_efuse_i2c(uint8_t benable)
+{
+    int ret = 0;
+    uint8_t rw_buffer[2];
+
+    /* set voltage to 2.0V */
+    rw_buffer[0] = 0x3;
+    ret = i2c_cust_write(0, 0x3F, 100, rw_buffer, 1);
+    if (ret)
+        return ret;
+    ret = i2c_cust_read(0, 0x3F, 100, &rw_buffer[1], 1);
+    if (ret)
+        return ret;
+    rw_buffer[1] |= (0x1A << 2);
+    LTRACEF("[enable_efuse]write: 0x%x: 0x%x\r\n", rw_buffer[0], rw_buffer[1]);
+    ret = i2c_cust_write(0, 0x3F, 100, rw_buffer, 2);
+    if (ret)
+        return ret;
+
+    /* enable/disable power */
+    rw_buffer[0] = 0x12;
+    ret = i2c_cust_write(0, 0x3F, 100, rw_buffer, 1);
+    if (ret)
+        return ret;
+    ret = i2c_cust_read(0, 0x3F, 100, &rw_buffer[1], 1);
+    if (ret)
+        return ret;
+    if (benable)
+        rw_buffer[1] |= (0x1 << 2);
+    else
+        rw_buffer[1] &= ~(0x1 << 2);
+
+    LTRACEF("[enable_efuse]write: 0x%x: 0x%x\r\n", rw_buffer[0], rw_buffer[1]);
+    ret = i2c_cust_write(0, 0x3F, 100, rw_buffer, 2);
+    if (ret)
+        return ret;
+    return ret;
+}
+
+void enable_vefuse(void)
+{
+    enable_efuse_i2c(1);
+    spin(10000);
+}
+
+void disable_vefuse(void)
+{
+    enable_efuse_i2c(0);
+    spin(10000);
+}
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/gpio/mt_gpio.c b/src/bsp/lk/platform/mediatek/mt2712/drivers/gpio/mt_gpio.c
new file mode 100644
index 0000000..b8eea91
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/gpio/mt_gpio.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <debug.h>
+#include <platform/mt_gpio.h>
+#include <platform/mt_reg_base.h>
+#include <reg.h>
+#include <string.h>
+#include <target/io_pwr.h>
+
+enum gpio_rdsel_type {
+    GPIO,   /* VCC3IO_GPIO */
+    GBE,    /* VCC3IO_GBE */
+    NOR,    /* VCC3IO_NOR */
+    MSDC1,  /* VCC3IO_MSDC1 */
+    MSDC2,  /* VCC3IO_MSDC2 */
+    UART4B, /* VCC3IO_UART4B */
+    SPICTP, /* VCC3IO_SPICTP */
+    SPIMCU, /* VCC3IO_SPIMCU */
+    I2S102, /* VCC3IO_I2S102 */
+    I2SIO1, /* VCC3IO_I2SIO1 */
+    I2SO02, /* VCC3IO_I2SO02 */
+    TDMO,   /* VCC3IO_TDMO */
+};
+
+int gpio_type[] = {
+    [GPIO] = VCC3IO_GPIO,
+    [GBE] = VCC3IO_GBE,
+    [NOR] = VCC3IO_NOR,
+    [MSDC1] = VCC3IO_MSDC1,
+    [MSDC2] = VCC3IO_MSDC2,
+    [UART4B] = VCC3IO_UART4B,
+    [SPICTP] = VCC3IO_SPICTP,
+    [SPIMCU] = VCC3IO_SPIMCU,
+    [I2S102] = VCC3IO_I2S102,
+    [I2SIO1] = VCC3IO_I2SIO1,
+    [I2SO02] = VCC3IO_I2SO02,
+    [TDMO] = VCC3IO_TDMO
+};
+
+static inline void gpio_set_rdsel(enum gpio_rdsel_type type, addr_t reg, int start_pos)
+{
+    u32 val = 0;
+
+    if (gpio_type[type] == 1) {
+        val |= 0x0c << start_pos;
+    }
+    writel((readl(reg) & (~(0x3f << start_pos))) | val, reg);
+}
+
+void mt_gpio_init(void)
+{
+    gpio_set_rdsel(GPIO, GPIO_RDSEL11_EN, 0);
+    gpio_set_rdsel(GPIO, GPIO_RDSEL11_EN, 6);
+    gpio_set_rdsel(GPIO, GPIO_EXMD_CTRL0, 4);
+    gpio_set_rdsel(GPIO, GPIO_RDSEL10_EN, 0);
+    gpio_set_rdsel(GPIO, GPIO_RDSEL10_EN, 6);
+    gpio_set_rdsel(GPIO, GPIO_RDSEL2_EN, 0);
+    gpio_set_rdsel(GPIO, GPIO_RDSEL2_EN, 6);
+    gpio_set_rdsel(GBE, GPIO_RDSEL3_EN, 0);
+    gpio_set_rdsel(GBE, GPIO_RDSEL3_EN, 6);
+    gpio_set_rdsel(GBE, GPIO_RDSEL2_EN, 0);
+    gpio_set_rdsel(GBE, GPIO_RDSEL2_EN, 6);
+    gpio_set_rdsel(GBE, GPIO_RDSEL4_EN, 0);
+    gpio_set_rdsel(NOR, GPIO_RDSEL1_EN, 6);
+    gpio_set_rdsel(MSDC1, GPIO_MSDC1_CTRL5, 4);
+    gpio_set_rdsel(MSDC2, GPIO_MSDC2_CTRL5, 4);
+    gpio_set_rdsel(UART4B, GPIO_RDSEL4_EN, 6);
+    gpio_set_rdsel(UART4B, GPIO_RDSEL5_EN, 0);
+    gpio_set_rdsel(SPICTP, GPIO_RDSEL6_EN, 0);
+    gpio_set_rdsel(SPICTP, GPIO_RDSEL6_EN, 6);
+    gpio_set_rdsel(SPIMCU, GPIO_RDSEL7_EN, 0);
+    gpio_set_rdsel(SPIMCU, GPIO_RDSEL7_EN, 6);
+    gpio_set_rdsel(I2S102, GPIO_RDSEL8_EN, 0);
+    gpio_set_rdsel(I2S102, GPIO_RDSEL8_EN, 6);
+    gpio_set_rdsel(I2S102, GPIO_RDSEL9_EN, 0);
+    gpio_set_rdsel(I2S102, GPIO_RDSEL9_EN, 6);
+    gpio_set_rdsel(I2SIO1, GPIO_RDSELA_EN, 0);
+    gpio_set_rdsel(I2SIO1, GPIO_RDSELA_EN, 6);
+    gpio_set_rdsel(I2SIO1, GPIO_RDSELB_EN, 0);
+    gpio_set_rdsel(I2SIO1, GPIO_RDSELB_EN, 6);
+    gpio_set_rdsel(I2SO02, GPIO_RDSELC_EN, 0);
+    gpio_set_rdsel(I2SO02, GPIO_RDSELC_EN, 6);
+    gpio_set_rdsel(I2SO02, GPIO_RDSELD_EN, 0);
+    gpio_set_rdsel(I2SO02, GPIO_RDSELD_EN, 6);
+    gpio_set_rdsel(I2SO02, GPIO_RDSELE_EN, 0);
+    gpio_set_rdsel(I2SO02, GPIO_RDSELE_EN, 6);
+    gpio_set_rdsel(TDMO, GPIO_RDSELF_EN, 0);
+    gpio_set_rdsel(TDMO, GPIO_RDSELF_EN, 6);
+}
+
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/key/mtk_key.c b/src/bsp/lk/platform/mediatek/mt2712/drivers/key/mtk_key.c
new file mode 100644
index 0000000..9f7e6a6
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/key/mtk_key.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <debug.h>
+#include <platform/mt_reg_base.h>
+#include <reg.h>
+
+#define GPIO_DIN2          (GPIO_BASE + 0x410)
+
+bool check_download_key(void)
+{
+#if !(CFG_FPGA_PLATFORM)
+    return (readl(GPIO_DIN2) & (1U << 3)) == 0;
+#else
+    return false;
+#endif
+}
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/mmc/mmc_core.c b/src/bsp/lk/platform/mediatek/mt2712/drivers/mmc/mmc_core.c
new file mode 100644
index 0000000..23d3ace
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/mmc/mmc_core.c
@@ -0,0 +1,2084 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*=======================================================================*/
+/* HEADER FILES                                                          */
+/*=======================================================================*/
+#include <config.h>
+#include <platform/msdc.h>
+#include <platform/mmc_core.h>
+#include <platform/mmc_rpmb.h>
+#include <platform/mmc_ioctl.h>
+#include <platform/mtk_bio_ioctl.h>
+#include <platform/trapping.h>
+#include <lib/bio.h>
+#include <lib/heap.h>
+#include <lib/partition.h>
+#include <pow2.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+#include <errno.h>
+#include <kernel/mutex.h>
+#include <trace.h>
+
+#define LOCAL_TRACE 0
+
+#define CMD_RETRIES        (5)
+#define CMD_TIMEOUT        (100)    /* 100ms */
+#define PAD_DELAY_MAX 32
+
+static int mmc_set_ext_csd(struct mmc_card *card, u8 addr, u8 value);
+/* before DRAM k, malloc() is not ready, so define it globally */
+struct mmc_host msdc_host0;
+struct mmc_card emmc_card;
+
+typedef struct {
+    bdev_t bdev;
+    u32 part_id;
+    struct mmc_host *host;
+    struct mmc_card *card;
+} mmc_dev_t;
+
+struct msdc_delay_phase {
+    u8 maxlen;
+    u8 start;
+    u8 final_phase;
+};
+
+static const unsigned int tran_exp[] = {
+    10000,      100000,     1000000,    10000000,
+    0,      0,      0,      0
+};
+
+static const unsigned char tran_mant[] = {
+    0,  10, 12, 13, 15, 20, 25, 30,
+    35, 40, 45, 50, 55, 60, 70, 80,
+};
+
+static const unsigned char mmc_tran_mant[] = {
+    0,  10, 12, 13, 15, 20, 26, 30,
+    35, 40, 45, 52, 55, 60, 70, 80,
+};
+
+static u32 unstuff_bits(u32 *resp, u32 start, u32 size)
+{
+    const u32 __mask = (1 << (size)) - 1;
+    const int __off = 3 - ((start) / 32);
+    const int __shft = (start) & 31;
+    u32 __res;
+
+    __res = resp[__off] >> __shft;
+    if ((size) + __shft >= 32)
+        __res |= resp[__off-1] << (32 - __shft);
+    return __res & __mask;
+}
+
+#define UNSTUFF_BITS(r,s,sz)    unstuff_bits(r,s,sz)
+
+static int mmc_switch_part(mmc_dev_t *dev)
+{
+    int err = MMC_ERR_NONE;
+    struct mmc_card *card;
+    struct mmc_host *host;
+    u8 cfg;
+
+    host = dev->host;
+    if (host->curr_part == dev->part_id)
+        /* already set to specific partition */
+        return MMC_ERR_NONE;
+
+    if (dev->part_id > EXT_CSD_PART_CFG_GP_PART_4) {
+        dprintf(CRITICAL, "[MSDC] Unsupported partid: %u\n", dev->part_id);
+        return MMC_ERR_INVALID;
+    }
+
+    card = dev->card;
+    ASSERT(card);
+
+    cfg = card->ext_csd.part_cfg;
+    cfg = (cfg & ~0x7) | dev->part_id;
+    err = mmc_set_ext_csd(card, EXT_CSD_PART_CFG, cfg);
+    if (err)
+        dprintf(CRITICAL, "[MSDC] switch to part %u failed!\n", dev->part_id);
+    else
+        card->ext_csd.part_cfg = cfg;
+
+    return err;
+}
+
+static int mmc_cmd(struct mmc_host *host, struct mmc_command *cmd)
+{
+    int err;
+    int retry = cmd->retries;
+
+    do {
+        err = msdc_cmd(host, cmd);
+        /* do not retry CMD21 */
+        if (err == MMC_ERR_NONE || cmd->opcode == MMC_CMD21)
+            break;
+    } while (retry--);
+
+    return err;
+}
+
+static int mmc_app_cmd(struct mmc_host *host, struct mmc_command *cmd,
+                       u32 rca, int retries)
+{
+    int err = MMC_ERR_FAILED;
+    struct mmc_command appcmd;
+
+    appcmd.opcode  = MMC_CMD_APP_CMD;
+    appcmd.arg     = rca << 16;
+    appcmd.rsptyp  = RESP_R1;
+    appcmd.retries = CMD_RETRIES;
+    appcmd.timeout = CMD_TIMEOUT;
+
+    do {
+        err = mmc_cmd(host, &appcmd);
+
+        if (err == MMC_ERR_NONE)
+            err = mmc_cmd(host, cmd);
+        if (err == MMC_ERR_NONE)
+            break;
+    } while (retries--);
+
+    return err;
+}
+
+static u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
+{
+    int bit;
+
+    ocr &= host->ocr_avail;
+
+    bit = __builtin_ffs(ocr);
+    if (bit) {
+        bit -= 1;
+        ocr &= 3 << bit;
+    } else {
+        ocr = 0;
+    }
+    return ocr;
+}
+
+static inline int mmc_go_idle(struct mmc_host *host)
+{
+    struct mmc_command cmd = {
+        MMC_CMD_GO_IDLE_STATE, 0, RESP_NONE, {0}, CMD_TIMEOUT, CMD_RETRIES, 0
+    };
+    return mmc_cmd(host, &cmd);
+}
+
+static int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
+{
+    struct mmc_command cmd;
+    int err;
+    static const u8 test_pattern = 0xAA;
+    u8 result_pattern;
+
+    /*
+     * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND
+     * before SD_APP_OP_COND. This command will harmlessly fail for
+     * SD 1.0 cards.
+     */
+
+    cmd.opcode  = SD_CMD_SEND_IF_COND;
+    cmd.arg     = ((ocr & 0xFF8000) != 0) << 8 | test_pattern;
+    cmd.rsptyp  = RESP_R1;
+    cmd.retries = 0;
+    cmd.timeout = CMD_TIMEOUT;
+
+    err = mmc_cmd(host, &cmd);
+
+    if (err != MMC_ERR_NONE)
+        return err;
+
+    result_pattern = cmd.resp[0] & 0xFF;
+
+    if (result_pattern != test_pattern)
+        return MMC_ERR_INVALID;
+
+    return MMC_ERR_NONE;
+}
+
+/*
+ * return MMC_ERR_RETRY means that need re-send CMD1 in stage 2
+ */
+static int mmc_send_op_cond_once(struct mmc_host *host, u32 ocr, u32 *rocr)
+{
+    int i, err = 0;
+    struct mmc_command cmd = {
+        MMC_CMD_SEND_OP_COND, 0, RESP_R3, {0}, CMD_TIMEOUT, 0, 0
+    };
+
+    cmd.arg = ocr;
+
+    for (i = 1; i; i--) {
+        err = mmc_cmd(host, &cmd);
+        if (err)
+            break;
+
+        /* if we're just probing, do a single pass */
+        if (ocr == 0)
+            break;
+
+        if (cmd.resp[0] & MMC_CARD_BUSY)
+            break;
+
+        err = MMC_ERR_RETRY;
+    }
+
+    if (!err && rocr)
+        *rocr = cmd.resp[0];
+
+    return err;
+}
+
+static int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
+{
+    int i, err = 0;
+    struct mmc_command cmd = {
+        MMC_CMD_SEND_OP_COND, 0, RESP_R3, {0}, CMD_TIMEOUT, 0, 0
+    };
+
+    cmd.arg = ocr;
+
+    for (i = 100; i; i--) {
+        err = mmc_cmd(host, &cmd);
+        if (err)
+            break;
+
+        /* if we're just probing, do a single pass */
+        if (ocr == 0)
+            break;
+
+        if (cmd.resp[0] & MMC_CARD_BUSY)
+            break;
+
+        err = MMC_ERR_TIMEOUT;
+
+        spin(10000);
+
+    }
+
+    if (!err && rocr)
+        *rocr = cmd.resp[0];
+
+    return err;
+}
+
+static int mmc_send_app_op_cond_once(struct mmc_host *host, u32 ocr, u32 *rocr)
+{
+    struct mmc_command cmd;
+    int i, err = 0;
+
+    cmd.opcode  = SD_ACMD_SEND_OP_COND;
+    cmd.arg     = ocr;
+    cmd.rsptyp  = RESP_R3;
+    cmd.retries = CMD_RETRIES;
+    cmd.timeout = CMD_TIMEOUT;
+
+    for (i = 1; i; i--) {
+        err = mmc_app_cmd(host, &cmd, 0, CMD_RETRIES);
+        if (err != MMC_ERR_NONE)
+            break;
+
+        if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
+            break;
+
+        err = MMC_ERR_RETRY;
+    }
+
+    if (rocr)
+        *rocr = cmd.resp[0];
+
+    return err;
+}
+
+static int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
+{
+    struct mmc_command cmd;
+    int i, err = 0;
+
+    cmd.opcode  = SD_ACMD_SEND_OP_COND;
+    cmd.arg     = ocr;
+    cmd.rsptyp  = RESP_R3;
+    cmd.retries = CMD_RETRIES;
+    cmd.timeout = CMD_TIMEOUT;
+
+    for (i = 100; i; i--) {
+        err = mmc_app_cmd(host, &cmd, 0, CMD_RETRIES);
+        if (err != MMC_ERR_NONE)
+            break;
+
+        if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
+            break;
+
+        err = MMC_ERR_TIMEOUT;
+
+        spin(10000);
+    }
+
+    if (rocr)
+        *rocr = cmd.resp[0];
+
+    return err;
+}
+
+static int mmc_all_send_cid(struct mmc_host *host, u32 *cid)
+{
+    int ret;
+    struct mmc_command cmd = {
+        MMC_CMD_ALL_SEND_CID, 0, RESP_R2, {0}, CMD_TIMEOUT, CMD_RETRIES, 0
+    };
+    ret = mmc_cmd(host, &cmd);
+    if (ret == MMC_ERR_NONE) {
+        memcpy(cid, &cmd.resp[0], sizeof(u32) * 4);
+        LTRACEF("eMMC CID: %08x%08x%08x%08x\n",
+                cid[0], cid[1], cid[2], cid[3]);
+    }
+
+    return ret;
+}
+
+static int mmc_send_relative_addr(struct mmc_host *host,
+                                  struct mmc_card *card, unsigned int *rca)
+{
+    int err;
+    struct mmc_command cmd;
+
+    memset(&cmd, 0, sizeof(struct mmc_command));
+
+    if (mmc_card_mmc(card)) { /* set rca */
+        cmd.opcode  = MMC_CMD_SET_RELATIVE_ADDR;
+        cmd.arg     = *rca << 16;
+        cmd.rsptyp  = RESP_R1;
+        cmd.retries = CMD_RETRIES;
+        cmd.timeout = CMD_TIMEOUT;
+    } else {  /* send rca */
+        cmd.opcode  = SD_CMD_SEND_RELATIVE_ADDR;
+        cmd.arg     = 0;
+        cmd.rsptyp  = RESP_R6;
+        cmd.retries = CMD_RETRIES;
+        cmd.timeout = CMD_TIMEOUT;
+    }
+    err = mmc_cmd(host, &cmd);
+    if ((err == MMC_ERR_NONE) && !mmc_card_mmc(card))
+        *rca = cmd.resp[0] >> 16;
+
+    return err;
+}
+
+static int mmc_select_card(struct mmc_host *host, struct mmc_card *card)
+{
+    struct mmc_command cmd = {
+        MMC_CMD_SELECT_CARD, 0, RESP_R1B, {0}, CMD_TIMEOUT, CMD_RETRIES, 0
+    };
+    cmd.arg = card->rca << 16;
+    return mmc_cmd(host, &cmd);
+}
+
+static int mmc_deselect_card(struct mmc_host *host, struct mmc_card *card)
+{
+    /* deslect cmd has no response */
+    struct mmc_command cmd = {
+        MMC_CMD_SELECT_CARD, 0, RESP_NONE, {0}, CMD_TIMEOUT, CMD_RETRIES, 0
+    };
+    return mmc_cmd(host, &cmd);
+}
+
+static int mmc_send_status(struct mmc_host *host, struct mmc_card *card,
+                           u32 *status)
+{
+    int err;
+    struct mmc_command cmd = {
+        MMC_CMD_SEND_STATUS, 0, RESP_R1, {0}, CMD_TIMEOUT, CMD_RETRIES, 0
+    };
+    cmd.arg = card->rca << 16;
+
+    err = mmc_cmd(host, &cmd);
+    if (err == MMC_ERR_NONE)
+        *status = cmd.resp[0];
+    return err;
+}
+
+static int mmc_switch(struct mmc_host *host, struct mmc_card *card,
+                      u8 set, u8 index, u8 value)
+{
+    int err;
+    u32 status = 0, count = 0;
+    struct mmc_command cmd = {
+        MMC_CMD_SWITCH, 0, RESP_R1B, {0}, CMD_TIMEOUT, CMD_RETRIES, 0
+    };
+
+    cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) |
+              (value << 8) | set;
+
+    err = mmc_cmd(host, &cmd);
+    if (err != MMC_ERR_NONE)
+        return err;
+
+    do {
+        err = mmc_send_status(host, card, &status);
+        if (err) {
+            dprintf(CRITICAL, "[eMMC] Fail to send status %d\n", err);
+            break;
+        }
+        if (status & R1_SWITCH_ERROR) {
+            dprintf(CRITICAL, "[eMMC] switch error. arg(0x%x)\n", cmd.arg);
+            return MMC_ERR_FAILED;
+        }
+        if (count++ >= 600000) {
+            dprintf(CRITICAL, "[%s]: timeout happend, count=%d, status=0x%x\n",
+                    __func__, count, status);
+            break;
+        }
+    } while (!(status & R1_READY_FOR_DATA) || (R1_CURRENT_STATE(status) == 7));
+
+    if (!err && (index == EXT_CSD_PART_CFG))
+        host->curr_part = value & 0x7;
+
+    return err;
+}
+
+static int mmc_read_csds(struct mmc_host *host, struct mmc_card *card)
+{
+    int err;
+    struct mmc_command cmd = {
+        MMC_CMD_SEND_CSD, 0, RESP_R2, {0}, CMD_TIMEOUT * 100, CMD_RETRIES, 0
+    };
+
+    cmd.arg = card->rca << 16;
+
+    err = mmc_cmd(host, &cmd);
+    if (err == MMC_ERR_NONE) {
+        unsigned int e, m;
+        card->csd.mmca_vsn = UNSTUFF_BITS(&cmd.resp[0], 122, 4);
+        m = UNSTUFF_BITS(&cmd.resp[0], 99, 4);
+        e = UNSTUFF_BITS(&cmd.resp[0], 96, 3);
+        card->csd.max_dtr = tran_exp[e] * mmc_tran_mant[m];
+        e = UNSTUFF_BITS(&cmd.resp[0], 47, 3);
+        m = UNSTUFF_BITS(&cmd.resp[0], 62, 12);
+        card->csd.capacity = (1 + m) << (e + 2);
+        card->csd.read_blkbits = UNSTUFF_BITS(&cmd.resp[0], 80, 4);
+        memcpy(&card->raw_csd, &cmd.resp[0], sizeof(u32) * 4);
+    }
+
+    return err;
+}
+
+static int mmc_decode_csd(struct mmc_card *card)
+{
+    struct mmc_csd *csd = &card->csd;
+    unsigned int e, m, csd_struct;
+    u32 *resp = card->raw_csd;
+
+    /* common part; some part are updated later according to spec. */
+    csd_struct = unstuff_bits(resp, 126, 2);
+    csd->csd_struct = csd_struct;
+
+    /* For MMC
+     * We only understand CSD structure v1.1 and v1.2.
+     * v1.2 has extra information in bits 15, 11 and 10.
+     */
+    if ( ( mmc_card_mmc(card) &&
+            ( csd_struct != CSD_STRUCT_VER_1_0 && csd_struct != CSD_STRUCT_VER_1_1
+              && csd_struct != CSD_STRUCT_VER_1_2 && csd_struct != CSD_STRUCT_EXT_CSD )
+         ) ||
+            ( mmc_card_sd(card) && ( csd_struct != 0 && csd_struct!=1 ) )
+       ) {
+        dprintf(ALWAYS, "Unknown CSD ver %d\n", csd_struct);
+        return MMC_ERR_INVALID;
+    }
+
+    m = unstuff_bits(resp, 99, 4);
+    e = unstuff_bits(resp, 96, 3);
+    csd->max_dtr      = tran_exp[e] * tran_mant[m];
+
+    /* update later according to spec. */
+    csd->read_blkbits = unstuff_bits(resp, 80, 4);
+
+    e = unstuff_bits(resp, 47, 3);
+    m = unstuff_bits(resp, 62, 12);
+    csd->capacity     = (1 + m) << (e + 2);
+
+    //Specific part
+    if (mmc_card_sd(card)) {
+        switch (csd_struct) {
+            case 0:
+                break;
+            case 1:
+                /*
+                 * This is a block-addressed SDHC card. Most
+                 * interesting fields are unused and have fixed
+                 * values. To avoid getting tripped by buggy cards,
+                 * we assume those fixed values ourselves.
+                 */
+                mmc_card_set_blockaddr(card);
+
+                m = unstuff_bits(resp, 48, 22);
+                csd->capacity     = (1 + m) << 10;
+
+                csd->read_blkbits = 9;
+                break;
+        }
+    } else {
+        csd->mmca_vsn    = unstuff_bits(resp, 122, 4);
+    }
+
+    return 0;
+}
+
+static void mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
+{
+    u32 caps = card->host->caps;
+    u8 card_type = ext_csd[EXT_CSD_CARD_TYPE];
+
+    card->ext_csd.sectors =
+        ext_csd[EXT_CSD_SEC_CNT + 0] << 0 |
+        ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
+        ext_csd[EXT_CSD_SEC_CNT + 2] << 16 |
+        ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
+
+    card->ext_csd.rev = ext_csd[EXT_CSD_REV];
+    card->ext_csd.boot_info   = ext_csd[EXT_CSD_BOOT_INFO];
+    card->ext_csd.boot_part_sz = ext_csd[EXT_CSD_BOOT_SIZE_MULT] * 128 * 1024;
+    card->ext_csd.rpmb_sz = ext_csd[EXT_CSD_RPMB_SIZE_MULT] * 128 * 1024;
+
+    if (card->ext_csd.sectors)
+        mmc_card_set_blockaddr(card);
+
+    if (caps & MMC_CAP_EMMC_HS400 &&
+            card_type & EXT_CSD_CARD_TYPE_HS400_1_8V) {
+        card->ext_csd.hs400_support = 1;
+        card->ext_csd.hs_max_dtr = 200000000;
+    } else if (caps & MMC_CAP_EMMC_HS200 &&
+               card_type & EXT_CSD_CARD_TYPE_HS200_1_8V) {
+        card->ext_csd.hs_max_dtr = 200000000;
+    } else if (caps & MMC_CAP_DDR &&
+               card_type & EXT_CSD_CARD_TYPE_DDR_52) {
+        card->ext_csd.ddr_support = 1;
+        card->ext_csd.hs_max_dtr = 52000000;
+    } else if (caps & MMC_CAP_MMC_HIGHSPEED &&
+               card_type & EXT_CSD_CARD_TYPE_52) {
+        card->ext_csd.hs_max_dtr = 52000000;
+    } else if (card_type & EXT_CSD_CARD_TYPE_26) {
+        card->ext_csd.hs_max_dtr = 26000000;
+    } else {
+        /* MMC v4 spec says this cannot happen */
+        dprintf(CRITICAL, "[eMMC] MMCv4 but HS unsupported\n");
+    }
+
+    card->ext_csd.part_cfg = ext_csd[EXT_CSD_PART_CFG];
+    card->ext_csd.sec_support = ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT];
+    card->ext_csd.reset_en = ext_csd[EXT_CSD_RST_N_FUNC];
+    card->ext_csd.hs_timing = ext_csd[EXT_CSD_HS_TIMING];
+
+    return;
+}
+
+/* Read and decode extended CSD. */
+static int mmc_read_ext_csd(struct mmc_host *host, struct mmc_card *card)
+{
+    int err = MMC_ERR_NONE;
+    u8 *ext_csd;
+    int result = MMC_ERR_NONE;
+    struct mmc_data data;
+    addr_t base = host->base;
+    struct mmc_command cmd = {
+        MMC_CMD_SEND_EXT_CSD, 0, RESP_R1, {0}, CMD_TIMEOUT, CMD_RETRIES, 0
+    };
+
+    if (card->csd.mmca_vsn < CSD_SPEC_VER_4) {
+        dprintf(CRITICAL, "[eMMC] MMCA_VSN: %d. Skip EXT_CSD\n",
+                card->csd.mmca_vsn);
+        return MMC_ERR_NONE;
+    }
+
+    /*
+     * As the ext_csd is so large and mostly unused, we don't store the
+     * raw block in mmc_card.
+     */
+    ext_csd = malloc(512);
+    ASSERT(ext_csd);
+    memset(ext_csd, 0, 512);
+
+    msdc_reset_tune_counter(host);
+
+    do {
+        MSDC_DMA_ON;
+        MSDC_CLR_FIFO();
+        MSDC_WRITE32(SDC_BLK_NUM, 1);
+        host->blklen = 512;
+        msdc_set_timeout(host, 100000000, 0);
+        err = mmc_cmd(host, &cmd);
+        if (err != MMC_ERR_NONE)
+            goto out;
+
+        data.cmd = &cmd;
+        data.blks = 1;
+        data.buf = ext_csd;
+        data.timeout = 100;
+        err = msdc_dma_transfer(host, &data);
+        MSDC_DMA_OFF;
+        if (err != MMC_ERR_NONE) {
+            if (msdc_abort_handler(host, 1))
+                dprintf(CRITICAL, "[eMMC] data abort failed\n");
+            result = msdc_tune_read(host);
+        }
+    } while (err && result != MMC_ERR_READTUNEFAIL);
+    msdc_reset_tune_counter(host);
+    mmc_decode_ext_csd(card, ext_csd);
+
+out:
+    free(ext_csd);
+    return err;
+}
+
+static void mmc_set_clock(struct mmc_host *host, int state, unsigned int hz)
+{
+    if (hz >= host->f_max) {
+        hz = host->f_max;
+    } else if (hz < host->f_min) {
+        hz = host->f_min;
+    }
+    msdc_config_clock(host, state, hz);
+}
+
+static int mmc_set_bus_width(struct mmc_host *host, struct mmc_card *card, int width)
+{
+    int err = MMC_ERR_NONE;
+    u32 arg = 0;
+    struct mmc_command cmd;
+
+    if (mmc_card_sd(card)) {
+        if (width == HOST_BUS_WIDTH_8) {
+            arg = SD_BUS_WIDTH_4;
+            width = HOST_BUS_WIDTH_4;
+        }
+
+        if ((width == HOST_BUS_WIDTH_4) && (host->caps & MMC_CAP_4_BIT_DATA)) {
+            arg = SD_BUS_WIDTH_4;
+        } else {
+            arg = SD_BUS_WIDTH_1;
+            width = HOST_BUS_WIDTH_1;
+        }
+
+        cmd.opcode  = SD_ACMD_SET_BUSWIDTH;
+        cmd.arg     = arg;
+        cmd.rsptyp  = RESP_R1;
+        cmd.retries = CMD_RETRIES;
+        cmd.timeout = CMD_TIMEOUT;
+
+        err = mmc_app_cmd(host, &cmd, card->rca, 0);
+        if (err != MMC_ERR_NONE)
+            goto out;
+
+        msdc_config_bus(host, width);
+    } else if (mmc_card_mmc(card)) {
+
+        if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
+            goto out;
+
+        if (width == HOST_BUS_WIDTH_8) {
+            if (host->caps & MMC_CAP_8_BIT_DATA) {
+                arg = EXT_CSD_BUS_WIDTH_8;
+            } else {
+                width = HOST_BUS_WIDTH_4;
+            }
+        }
+        if (width == HOST_BUS_WIDTH_4) {
+            if (host->caps & MMC_CAP_4_BIT_DATA) {
+                arg = EXT_CSD_BUS_WIDTH_4;
+            } else {
+                width = HOST_BUS_WIDTH_1;
+            }
+        }
+        if (width == HOST_BUS_WIDTH_1)
+            arg = EXT_CSD_BUS_WIDTH_1;
+
+        err = mmc_switch(host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, arg);
+        if (err != MMC_ERR_NONE) {
+            dprintf(CRITICAL, "[eMMC] Switch to bus width(%d) failed\n", arg);
+            goto out;
+        }
+        mmc_card_clr_ddr(card);
+
+        msdc_config_bus(host, width);
+    }
+
+out:
+    return err;
+}
+
+static u32 test_delay_bit(u32 delay, u32 bit)
+{
+    bit %= PAD_DELAY_MAX;
+    return delay & (1 << bit);
+}
+
+static int get_delay_len(u32 delay, u32 start_bit)
+{
+    u32 i;
+
+    for (i = 0; i < (PAD_DELAY_MAX - start_bit); i++) {
+        if (test_delay_bit(delay, start_bit + i) == 0)
+            return i;
+    }
+    return PAD_DELAY_MAX - start_bit;
+}
+
+static struct msdc_delay_phase get_best_delay(u32 delay)
+{
+    int start = 0, len = 0;
+    int start_final = 0, len_final = 0;
+    u8 final_phase = 0xff;
+    struct msdc_delay_phase delay_phase = { .start = 0 };
+
+    if (delay == 0) {
+        dprintf(CRITICAL, "phase error: [map:%x]\n", delay);
+        delay_phase.final_phase = final_phase;
+        return delay_phase;
+    }
+
+    while (start < PAD_DELAY_MAX) {
+        len = get_delay_len(delay, start);
+        if (len_final < len) {
+            start_final = start;
+            len_final = len;
+        }
+        start += len ? len : 1;
+        if (len >= 12 && start_final < 4)
+            break;
+    }
+
+    /* The rule is that to find the smallest delay cell */
+    if (start_final == 0)
+        final_phase = (start_final + len_final / 3) % PAD_DELAY_MAX;
+    else
+        final_phase = (start_final + len_final / 2) % PAD_DELAY_MAX;
+    dprintf(ALWAYS, "phase: [map:%x] [maxlen:%d] [final:%d]\n",
+            delay, len_final, final_phase);
+
+    delay_phase.maxlen = len_final;
+    delay_phase.start = start_final;
+    delay_phase.final_phase = final_phase;
+    return delay_phase;
+}
+
+static int mmc_hs200_tune_cmd(struct mmc_host *host, int *cmd_error)
+{
+    int err = MMC_ERR_NONE;
+    u8 *tune_data;
+    u16 data_len = host->caps &  MMC_CAP_8_BIT_DATA ? 128: 64;
+    struct mmc_data data;
+    addr_t base = host->base;
+    struct mmc_command cmd = {
+        MMC_CMD21, 0, RESP_R1, {0}, CMD_TIMEOUT, 0, 0
+    };
+
+    tune_data = malloc(data_len);
+    ASSERT(tune_data);
+    memset(tune_data, 0, data_len);
+    *cmd_error = MMC_ERR_NONE;
+
+    msdc_reset_tune_counter(host);
+
+    MSDC_DMA_ON;
+    MSDC_CLR_FIFO();
+    MSDC_WRITE32(SDC_BLK_NUM, 1);
+    host->blklen = data_len;
+    msdc_set_timeout(host, 100000000, 0);
+    err = mmc_cmd(host, &cmd);
+    if (err != MMC_ERR_NONE)
+        *cmd_error = err; /* still need receive data, or will impact the next cmd21 */
+
+    data.cmd = &cmd;
+    data.blks = 1;
+    data.buf = tune_data;
+    data.timeout = 100;
+    err = msdc_dma_transfer(host, &data);
+    MSDC_DMA_OFF;
+    msdc_reset_tune_counter(host);
+
+out:
+    free(tune_data);
+    return err;
+}
+
+static int msdc_tune_together(struct mmc_host *mmc)
+{
+    addr_t base = mmc->base;
+    u32 rise_delay = 0, fall_delay = 0;
+    struct msdc_delay_phase final_rise_delay,
+            final_fall_delay = { .start = 0 };
+    u8 final_delay, final_maxlen;
+    int cmd_err;
+    int i;
+    int ret;
+
+    MSDC_CLR_BIT32(MSDC_IOCON, MSDC_IOCON_RSPL);
+    MSDC_CLR_BIT32(MSDC_IOCON, MSDC_IOCON_DSPL | MSDC_IOCON_W_D_SMPL);
+    for (i = 0 ; i < PAD_DELAY_MAX; i++) {
+        MSDC_SET_FIELD(MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRDLY, i);
+        MSDC_SET_FIELD(MSDC_PAD_TUNE, MSDC_PAD_TUNE_DATRRDLY, i);
+        ret = mmc_hs200_tune_cmd(mmc, &cmd_err);
+        if (!ret && !cmd_err)
+            rise_delay |= (1 << i);
+    }
+    final_rise_delay = get_best_delay(rise_delay);
+    /* if rising edge has enough margin, then do not scan falling edge */
+    if (final_rise_delay.maxlen >= 12 ||
+            (final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
+        goto skip_fall;
+
+    MSDC_SET_BIT32(MSDC_IOCON, MSDC_IOCON_RSPL);
+    MSDC_SET_BIT32(MSDC_IOCON, MSDC_IOCON_DSPL | MSDC_IOCON_W_D_SMPL);
+    for (i = 0; i < PAD_DELAY_MAX; i++) {
+        MSDC_SET_FIELD(MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRDLY, i);
+        MSDC_SET_FIELD(MSDC_PAD_TUNE, MSDC_PAD_TUNE_DATRRDLY, i);
+        ret = mmc_hs200_tune_cmd(mmc, &cmd_err);
+        if (!ret && !cmd_err)
+            fall_delay |= (1 << i);
+    }
+    final_fall_delay = get_best_delay(fall_delay);
+
+skip_fall:
+    final_maxlen = MAX(final_rise_delay.maxlen, final_fall_delay.maxlen);
+    if (final_maxlen == final_rise_delay.maxlen) {
+        MSDC_CLR_BIT32(MSDC_IOCON, MSDC_IOCON_RSPL);
+        MSDC_CLR_BIT32(MSDC_IOCON, MSDC_IOCON_DSPL | MSDC_IOCON_W_D_SMPL);
+        MSDC_SET_FIELD(MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRDLY,
+                       final_rise_delay.final_phase);
+        MSDC_SET_FIELD(MSDC_PAD_TUNE, MSDC_PAD_TUNE_DATRRDLY,
+                       final_rise_delay.final_phase);
+        final_delay = final_rise_delay.final_phase;
+    } else {
+        MSDC_SET_BIT32(MSDC_IOCON, MSDC_IOCON_RSPL);
+        MSDC_SET_BIT32(MSDC_IOCON, MSDC_IOCON_DSPL | MSDC_IOCON_W_D_SMPL);
+        MSDC_SET_FIELD(MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRDLY,
+                       final_fall_delay.final_phase);
+        MSDC_SET_FIELD(MSDC_PAD_TUNE, MSDC_PAD_TUNE_DATRRDLY,
+                       final_fall_delay.final_phase);
+        final_delay = final_fall_delay.final_phase;
+    }
+
+    dprintf(ALWAYS, "Final cmd/data pad delay: %x\n", final_delay);
+    return final_delay == 0xff ? -EIO : 0;
+}
+
+static int mmc_select_hs200(struct mmc_card *card)
+{
+    struct mmc_host *host = card->host;
+    int ret;
+
+    ret = mmc_set_bus_width(host, card, HOST_BUS_WIDTH_8);
+    if (ret != MMC_ERR_NONE) {
+        dprintf(CRITICAL, "failed to set bus width!\n");
+        return ret;
+    }
+
+    ret = mmc_switch(host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
+                     EXT_CSD_HS_TIMEING_HS200);
+    if (ret != MMC_ERR_NONE) {
+        dprintf(CRITICAL, "failed to switch to hs200 mode!\n");
+        return ret;
+    }
+
+    mmc_card_set_hs200(card);
+    mmc_set_clock(host, card->state, card->ext_csd.hs_max_dtr);
+
+    return 0;
+}
+
+static int mmc_select_hs400(struct mmc_card *card)
+{
+    struct mmc_host *host = card->host;
+    addr_t base = host->base;
+    int ret;
+
+    mmc_set_clock(host, card->state, 50000000);
+    ret = mmc_switch(host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
+                     EXT_CSD_HS_TIMEING_HS);
+    if (ret != MMC_ERR_NONE) {
+        dprintf(CRITICAL, "switch to high-speed from hs200 failed, err:%d\n", ret);
+        return ret;
+    }
+
+    ret = mmc_switch(host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_8_DDR);
+    if (ret != MMC_ERR_NONE) {
+        dprintf(CRITICAL, "switch to bus width for hs400 failed, err:%d\n", ret);
+        return ret;
+    }
+
+    ret = mmc_switch(host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
+                     EXT_CSD_HS_TIMEING_HS400);
+    if (ret != MMC_ERR_NONE) {
+        dprintf(CRITICAL, "switch to hs400 failed, err:%d\n", ret);
+        return ret;
+    }
+    mmc_card_set_hs400(card);
+    mmc_set_clock(host, card->state, card->ext_csd.hs_max_dtr);
+
+    /*
+     * Apply hs400 settings:
+     * set data tune to default value and apply ds delay setting
+     */
+
+    MSDC_CLR_BIT32(MSDC_IOCON, MSDC_IOCON_DSPL | MSDC_IOCON_W_D_SMPL);
+    MSDC_SET_FIELD(MSDC_PAD_TUNE, MSDC_PAD_TUNE_DATRRDLY, 0);
+    MSDC_WRITE32(EMMC50_PAD_DS_TUNE, 0x14029);
+
+    return ret;
+}
+
+static int mmc_hs200_tuning(struct mmc_card *card)
+{
+    struct mmc_host *host = card->host;
+    int ret;
+
+    ret = msdc_tune_together(host);
+    if (ret == -EIO) {
+        dprintf(CRITICAL, "hs200 tuning cmd/data error!\n");
+        return ret;
+    }
+
+    return MMC_ERR_NONE;
+}
+
+static int mmc_erase_start(struct mmc_card *card, u32 blknr)
+{
+    struct mmc_command cmd = {
+        MMC_CMD_ERASE_GROUP_START, 0, RESP_R1, {0}, CMD_TIMEOUT, 3, 0
+    };
+    if (mmc_card_sd(card))
+        cmd.opcode = MMC_CMD_ERASE_WR_BLK_START;
+    cmd.arg = blknr;
+    return mmc_cmd(card->host, &cmd);
+}
+
+static int mmc_erase_end(struct mmc_card *card, u32 blknr)
+{
+    struct mmc_command cmd = {
+        MMC_CMD_ERASE_GROUP_END, 0, RESP_R1, {0}, CMD_TIMEOUT, 3, 0
+    };
+    if (mmc_card_sd(card))
+        cmd.opcode = MMC_CMD_ERASE_WR_BLK_END;
+    cmd.arg = blknr;
+    return mmc_cmd(card->host, &cmd);
+}
+
+static int mmc_erase(struct mmc_card *card, u32 arg)
+{
+    int err;
+    u32 status;
+    struct mmc_command cmd = {
+        MMC_CMD_ERASE, 0, RESP_R1B, {0}, CMD_TIMEOUT, 3, 0
+    };
+    if (mmc_card_sd(card))
+        arg = 0;
+    cmd.arg = arg;
+
+    if (arg & MMC_ERASE_SECURE_REQ) {
+        if (!(card->ext_csd.sec_support & EXT_CSD_SEC_FEATURE_ER_EN))
+            return MMC_ERR_INVALID;
+    }
+    if ((arg & MMC_ERASE_GC_REQ) || (arg & MMC_ERASE_TRIM)) {
+        if (!(card->ext_csd.sec_support & EXT_CSD_SEC_FEATURE_GB_CL_EN))
+            return MMC_ERR_INVALID;
+    }
+
+    err = mmc_cmd(card->host, &cmd);
+    if (err)
+        return err;
+
+    do {
+        err = mmc_send_status(card->host, card, &status);
+        if (err)
+            break;
+        if (R1_STATUS(status) != 0)
+            break;
+    } while (R1_CURRENT_STATE(status) == 7);
+
+    return err;
+}
+
+static int mmc_do_trim(struct mmc_card *card, off_t start_addr, size_t len)
+{
+    int err = MMC_ERR_NONE;
+    off_t end_addr;
+
+    if (len < card->blklen) {
+        dprintf(CRITICAL, "%s: invalid len: %ld\n", __func__, len);
+        return MMC_ERR_INVALID;
+    }
+
+    end_addr =((start_addr + len) / card->blklen - 1) * card->blklen;
+
+    if (mmc_card_highcaps(card)) {
+        start_addr >>= MMC_BLOCK_BITS_SHFT;
+        end_addr >>= MMC_BLOCK_BITS_SHFT;
+    }
+
+    err = mmc_erase_start(card, start_addr);
+    if (err)
+        goto error;
+
+    err = mmc_erase_end(card, end_addr);
+    if (err)
+        goto error;
+
+    err = mmc_erase(card, MMC_ERASE_TRIM);
+
+error:
+    if (err)
+        dprintf(CRITICAL, "%s: erase range (0x%llx~0x%llx) failed,Err<%d>\n",
+                __func__, start_addr, end_addr, err);
+
+    return err;
+}
+
+static int mmc_set_ext_csd(struct mmc_card *card, u8 addr, u8 value)
+{
+    int err;
+
+    /* can't write */
+    if (192 <= addr || !card)
+        return MMC_ERR_INVALID;
+
+    err = mmc_switch(card->host, card, EXT_CSD_CMD_SET_NORMAL, addr, value);
+
+    if (err == MMC_ERR_NONE)
+        err = mmc_read_ext_csd(card->host, card);
+
+    return err;
+}
+
+static int mmc_set_reset_func(struct mmc_card *card, u8 enable)
+{
+    int err = MMC_ERR_FAILED;
+
+    if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
+        goto out;
+
+    if (card->ext_csd.reset_en == 0) {
+        err = mmc_set_ext_csd(card, EXT_CSD_RST_N_FUNC, enable);
+        if (err == MMC_ERR_NONE)
+            card->ext_csd.reset_en = enable;
+    } else {
+        /* no need set */
+        return MMC_ERR_NONE;
+    }
+out:
+    return err;
+}
+
+static int mmc_set_boot_bus(struct mmc_card *card, u8 rst_bwidth, u8 mode, u8 bwidth)
+{
+    int err = MMC_ERR_FAILED;
+    u8 arg;
+
+    if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
+        goto out;
+
+    arg = mode | rst_bwidth | bwidth;
+
+    err = mmc_set_ext_csd(card, EXT_CSD_BOOT_BUS_WIDTH, arg);
+out:
+    return err;
+}
+
+int mmc_get_boot_part(u32 *bootpart)
+{
+    struct mmc_host *host;
+    struct mmc_card *card;
+    int err = MMC_ERR_NONE;
+    u8 part_config;
+
+    host = &msdc_host0;
+    card = &emmc_card;
+
+    if (!bootpart)
+        return -1;
+
+    if (!card || !host)
+        return -1;
+    err = mmc_read_ext_csd(host, card);
+
+    if (err == MMC_ERR_NONE) {
+        part_config = (card->ext_csd.part_cfg >> 3) & 0x07;
+        if (part_config == 1) {
+            *bootpart = EMMC_PART_BOOT1;
+        } else if (part_config == 2) {
+            *bootpart = EMMC_PART_BOOT2;
+        } else if (part_config == 7) {
+            *bootpart = EMMC_PART_USER;
+        } else {
+            dprintf(CRITICAL, "[eMMC] Fail to get boot part\n");
+            return -1;
+        }
+    }
+
+    return err;
+}
+
+int mmc_set_boot_part(u32 bootpart)
+{
+    struct mmc_host *host;
+    struct mmc_card *card;
+    int err = MMC_ERR_NONE;
+    u8 part_config;
+    u8 cur_bootpart;
+
+    host = &msdc_host0;
+    card = &emmc_card;
+
+    if (bootpart !=  EMMC_PART_BOOT1
+            && bootpart !=  EMMC_PART_BOOT2
+            && bootpart !=  EMMC_PART_USER)
+        return -1;
+
+    if (!card || !host)
+        return -1;
+
+    err = mmc_read_ext_csd(host, card);
+    if (err != MMC_ERR_NONE)
+        goto out;
+
+    part_config = card->ext_csd.part_cfg;
+    cur_bootpart = (card->ext_csd.part_cfg >> 3) & 0x07;
+
+    if (((u32)cur_bootpart) == bootpart)
+        goto out;
+    LTRACEF("[SD0] Current boot part is %d\n", cur_bootpart);
+
+    part_config = (part_config & (~(0x7 << 3))) | (bootpart << 3);
+    LTRACEF("[SD0] Set boot part as %d\n", bootpart);
+    err = mmc_switch(host, card,
+                     EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CFG, part_config);
+
+out:
+    return err;
+}
+
+static int mmc_set_part_config(struct mmc_card *card, u8 cfg)
+{
+    int err = MMC_ERR_FAILED;
+
+    if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
+        goto out;
+
+    err = mmc_set_ext_csd(card, EXT_CSD_PART_CFG, cfg);
+out:
+    return err;
+}
+
+static int mmc_boot_config(struct mmc_card *card, u8 acken, u8 enpart, u8 buswidth, u8 busmode)
+{
+    int err = MMC_ERR_FAILED;
+    u8 val;
+    u8 rst_bwidth = 0;
+    u8 cfg;
+
+    if (card->csd.mmca_vsn < CSD_SPEC_VER_4 ||
+            !card->ext_csd.boot_info || card->ext_csd.rev < 3)
+        goto out;
+
+    cfg = card->ext_csd.part_cfg;
+    /* configure boot partition */
+    val = acken | (cfg & 0x3F);
+    err = mmc_set_part_config(card, val);
+    if (err != MMC_ERR_NONE)
+        goto out;
+    else
+        card->ext_csd.part_cfg = val;
+
+    /* configure boot bus mode and width */
+    rst_bwidth = (buswidth != EXT_CSD_BOOT_BUS_WIDTH_1 ? 1 : 0) << 2;
+    LTRACEF(" =====Set boot Bus Width<%d>=======\n", buswidth);
+    LTRACEF(" =====Set boot Bus mode<%d>=======\n", busmode);
+    err = mmc_set_boot_bus(card, rst_bwidth, busmode, buswidth);
+out:
+
+    return err;
+}
+
+static int emmc_boot_prepare(struct mmc_card *card)
+{
+    int err = MMC_ERR_NONE;
+    u8 buswidth = EXT_CSD_BOOT_BUS_WIDTH_1;
+
+    if (TRAPPING_IS_EMMC_BOOT_BUS_WIDTH_8BIT())
+        buswidth = EXT_CSD_BOOT_BUS_WIDTH_8;
+
+    err = mmc_boot_config(card, EXT_CSD_PART_CFG_EN_ACK,
+                          EXT_CSD_PART_CFG_EN_BOOT_PART_1,
+                          buswidth, EXT_CSD_BOOT_BUS_MODE_DEFT);
+    if (err)
+        goto exit;
+
+    err = mmc_set_reset_func(card, 1);
+exit:
+    return err;
+}
+
+static int mmc_dev_bread(struct mmc_card *card, unsigned long blknr, u32 blkcnt, u8 *dst)
+{
+    struct mmc_host *host = card->host;
+    u32 blksz = host->blklen;
+    int tune = 0;
+    int retry = 3;
+    int err;
+    unsigned long src;
+
+    src = mmc_card_highcaps(card) ? blknr : blknr * blksz;
+
+    do {
+        if (!tune) {
+            err = host->blk_read(host, (uchar *)dst, src, blkcnt);
+        } else {
+#ifdef FEATURE_MMC_RD_TUNING
+            err = msdc_tune_bread(host, (uchar *)dst, src, blkcnt);
+#endif
+            if (err && (host->sclk > (host->f_max >> 4)))
+                mmc_set_clock(host, card->state, host->sclk >> 1);
+        }
+        if (err == MMC_ERR_NONE) {
+            break;
+        }
+
+        if (err == MMC_ERR_BADCRC || err == MMC_ERR_ACMD_RSPCRC || err == MMC_ERR_CMD_RSPCRC) {
+            tune = 1;
+            retry++;
+        } else if (err == MMC_ERR_READTUNEFAIL || err == MMC_ERR_CMDTUNEFAIL) {
+            dprintf(CRITICAL, "[eMMC] Fail to tuning,%s",
+                    (err == MMC_ERR_CMDTUNEFAIL) ?
+                    "cmd tune failed!\n" : "read tune failed!\n");
+            break;
+        }
+    } while (retry--);
+
+    return err;
+}
+
+static int mmc_dev_bwrite(struct mmc_card *card, unsigned long blknr,
+                          u32 blkcnt, const u8 *src)
+{
+    struct mmc_host *host = card->host;
+    u32 blksz = host->blklen;
+    u32 status;
+    int tune = 0;
+    int retry = 3;
+    int err;
+    unsigned long dst;
+
+    dst = mmc_card_highcaps(card) ? blknr : blknr * blksz;
+
+    do {
+        if (!tune) {
+            err = host->blk_write(host, dst, (uchar *)src, blkcnt);
+        } else {
+#ifdef FEATURE_MMC_WR_TUNING
+            err = msdc_tune_bwrite(host, dst, (uchar *)src, blkcnt);
+#endif
+            if (err && (host->sclk > (host->f_max >> 4)))
+                mmc_set_clock(host, card->state, host->sclk >> 1);
+        }
+        if (err == MMC_ERR_NONE) {
+            do {
+                err = mmc_send_status(host, card, &status);
+                if (err) {
+                    dprintf(CRITICAL, "[eMMC] Fail to send status %d\n", err);
+                    break;
+                }
+            } while (!(status & R1_READY_FOR_DATA) ||
+                     (R1_CURRENT_STATE(status) == 7));
+            LTRACEF("[eMMC] Write %d bytes (DONE)\n", blkcnt * blksz);
+            break;
+        }
+
+        if (err == MMC_ERR_BADCRC || err == MMC_ERR_ACMD_RSPCRC || err == MMC_ERR_CMD_RSPCRC) {
+            tune = 1;
+            retry++;
+        } else if (err == MMC_ERR_WRITETUNEFAIL || err == MMC_ERR_CMDTUNEFAIL) {
+            dprintf(CRITICAL, "[eMMC] Fail to tuning,%s",
+                    (err == MMC_ERR_CMDTUNEFAIL) ?
+                    "cmd tune failed!\n" : "write tune failed!\n");
+            break;
+        }
+    } while (retry--);
+
+    return err;
+}
+
+static ssize_t mmc_block_read(struct bdev *dev, void *buf, bnum_t block,
+                              uint count)
+{
+    mmc_dev_t *__dev = (mmc_dev_t *)dev;
+    struct mmc_host *host = __dev->host;
+    struct mmc_card *card = __dev->card;
+    u32 maxblks = host->max_phys_segs;
+    u32 leftblks, totalblks = count;
+    ssize_t ret = 0;
+
+    mutex_acquire(&host->lock);
+    if (mmc_switch_part(__dev)) {
+        ret = ERR_IO;
+        goto done;
+    }
+
+    do {
+        leftblks = ((count > maxblks) ? maxblks : count);
+        if (mmc_dev_bread(card, (unsigned long)block, leftblks, buf)) {
+            ret = ERR_IO;
+            goto done;
+        }
+        block += leftblks;
+        buf += maxblks * dev->block_size;
+        count -= leftblks;
+    } while (count);
+
+    if (dev->block_size * totalblks > 0x7fffffffU)
+        /* ssize_t is defined as signed, should take a look here */
+        dprintf(CRITICAL, "[MSDC] %s: WARN! The return size is overflow! 0x%lx\n",
+                __func__, dev->block_size * totalblks);
+
+done:
+    mutex_release(&host->lock);
+    return ret ? ret : (ssize_t)dev->block_size * totalblks;
+}
+
+static ssize_t mmc_block_write(struct bdev *dev, const void *buf, bnum_t block,
+                               uint count)
+{
+    mmc_dev_t *__dev = (mmc_dev_t *)dev;
+    struct mmc_host *host = __dev->host;
+    struct mmc_card *card = __dev->card;
+    u32 maxblks = host->max_phys_segs;
+    u32 leftblks, totalblks = count;
+    ssize_t ret = 0;
+
+    mutex_acquire(&host->lock);
+    if (mmc_switch_part(__dev)) {
+        ret = ERR_IO;
+        goto done;
+    }
+
+    do {
+        leftblks = ((count > maxblks) ? maxblks : count);
+        if (mmc_dev_bwrite(card, (unsigned long)block, leftblks, buf)) {
+            ret = ERR_IO;
+            goto done;
+        }
+        block += leftblks;
+        buf = (u8 *)buf + maxblks * dev->block_size;
+        count -= leftblks;
+    } while (count);
+
+    if (dev->block_size * totalblks > 0x7fffffffU)
+        /* ssize_t is defined as signed, should take a look here */
+        dprintf(CRITICAL, "[MSDC] %s: WARN! The return size is overflow! 0x%lx\n",
+                __func__, dev->block_size * totalblks);
+
+done:
+    mutex_release(&host->lock);
+    return ret ? ret: (ssize_t)dev->block_size * totalblks;
+}
+
+static ssize_t mmc_wrap_erase(struct bdev *bdev, off_t offset, size_t len)
+{
+    mmc_dev_t *dev = (mmc_dev_t *)bdev;
+    struct mmc_host *host = dev->host;
+    ssize_t ret = 0;
+
+    mutex_acquire(&host->lock);
+    if (mmc_switch_part(dev)) {
+        ret = ERR_IO;
+        goto done;
+    }
+
+    /* ATTENTION:
+     * We use TRIM here, which is block-based(512B) wipping,
+     * If using ERASE here, please ensure the offset & size are
+     * erase-group aligned,
+     * OTHERWISE, some valid data may be wiped. refer to JEDEC spec:
+     * The Device will ignore all LSB's below the Erase Group size,
+     * effectively ROUNDING the address DOWN to the Erase Group boundary. */
+    ASSERT(dev && len);
+    if ((offset % MMC_BLOCK_SIZE) || (len % MMC_BLOCK_SIZE)) {
+        dprintf(CRITICAL, "%s: offset(0x%llx)/len(%lu) is not block-aligned!\n",
+                __func__, offset, len);
+        ret = ERR_IO;
+        goto done;
+    }
+
+    ASSERT(dev->card);
+    if (mmc_do_trim(dev->card, offset, len)) {
+        ret = ERR_IO;
+        goto done;
+    }
+
+done:
+    mutex_release(&host->lock);
+    return ret ? ret: (ssize_t)len;
+}
+
+static ssize_t mmc_rpmb_dummy_read(struct bdev *dev, void *buf, bnum_t block,
+                                   uint count)
+{
+    return 0;
+}
+
+static ssize_t mmc_rpmb_dummy_write(struct bdev *dev, const void *buf, bnum_t block,
+                                    uint count)
+{
+    return 0;
+}
+
+static ssize_t mmc_rpmb_dummy_erase(struct bdev *bdev, off_t offset, size_t len)
+{
+    return 0;
+}
+
+static int mmc_set_block_count(struct mmc_host *host, unsigned int blockcount,
+                               bool is_rel_write)
+{
+    struct mmc_command cmd = { .opcode = 0 };
+
+    cmd.opcode = MMC_CMD_SET_BLOCK_COUNT;
+    cmd.arg = blockcount & 0x0000FFFF;
+    if (is_rel_write)
+        cmd.arg |= 1 << 31;
+    cmd.rsptyp = RESP_R1;
+
+    return mmc_cmd(host, &cmd);
+}
+
+static int mmc_rpmb_ioctl_cmd(struct bdev *dev, struct mmc_ioc_cmd *arg)
+{
+    mmc_dev_t *__dev = (mmc_dev_t *)dev;
+    struct mmc_host *host = __dev->host;
+    //struct mmc_card *card = __dev->card;
+    struct mmc_command cmd = { .opcode = 0 };
+    struct mmc_data data = { .buf = NULL };
+    addr_t base = host->base;
+    int ret = 0;
+    u32 status;
+    int old_autocmd = msdc_get_autocmd(host);
+
+    msdc_set_autocmd(host, 0);
+    cmd.opcode = arg->opcode;
+    cmd.arg = arg->arg;
+    cmd.rsptyp = arg->flags; /* arg->flags must be type of enum of RESP_NONE ~ RESP_R1B */
+
+    if (arg->blocks) {
+        ret = mmc_set_block_count(host, arg->blocks,
+                                  arg->write_flag & (1 << 31));
+        if (ret != MMC_ERR_NONE) {
+            dprintf(CRITICAL, "mmc cmd23 failed!\n");
+            goto out;
+        }
+    }
+
+    if (arg->blocks) {
+        MSDC_DMA_ON;
+        MSDC_CLR_FIFO();
+        MSDC_WRITE32(SDC_BLK_NUM, arg->blocks);
+        host->blklen = 512;
+        msdc_set_timeout(host, 100000000, 0);
+        ret = mmc_cmd(host, &cmd);
+        if (ret != MMC_ERR_NONE) {
+            dprintf(CRITICAL, "mmc cmd failed\n");
+            goto out;
+        }
+
+        data.cmd = &cmd;
+        data.blks = arg->blocks;
+        data.buf = (uchar *)arg->data_ptr;
+        data.timeout = 100;
+        ret = msdc_dma_transfer(host, &data);
+        MSDC_DMA_OFF;
+
+    } else {
+        ret = mmc_cmd(host, &cmd);
+    }
+
+    if (ret == MMC_ERR_NONE && arg->write_flag & (1 << 31)) {
+        /* should use CMD13 to polling busy status */
+        do {
+            ret = mmc_send_status(host, host->card, &status);
+            if (ret) {
+                dprintf(CRITICAL, "error %d sending CMD13!\n", ret);
+                break;
+            }
+        } while (R1_CURRENT_STATE(status) == 7);
+    }
+out:
+    msdc_set_autocmd(host, old_autocmd);
+    return ret;
+}
+
+static int mmc_rpmb_ioctl(struct bdev *dev, int request, void *argp)
+{
+    mmc_dev_t *__dev = (mmc_dev_t *)dev;
+    struct mmc_host *host = __dev->host;
+    int ret = 0;
+
+    mutex_acquire(&host->lock);
+    if (mmc_switch_part(__dev)) {
+        ret = ERR_IO;
+        goto done;
+    }
+
+    switch ((unsigned int)request) {
+        case BIO_IOCTL_MMC_IOC_CMD:
+            ret = mmc_rpmb_ioctl_cmd(dev, (struct mmc_ioc_cmd *)argp);
+            break;
+        default:
+            ret = ERR_INVALID_ARGS;
+            break;
+    }
+
+done:
+    mutex_release(&host->lock);
+    return ret;
+}
+
+static int mmc_ioctl(struct bdev *dev, int request, void *argp)
+{
+    mmc_dev_t *__dev = (mmc_dev_t *)dev;
+    struct mmc_host *host = __dev->host;
+    int ret = 0;
+
+    mutex_acquire(&host->lock);
+    if (mmc_switch_part(__dev)) {
+        ret = ERR_IO;
+        goto done;
+    }
+
+    switch (request) {
+        case BIO_IOCTL_MMC_SET_BOOT:
+            ret = mmc_set_boot_part(*(u32 *)argp);
+            break;
+        case BIO_IOCTL_MMC_GET_BOOT:
+            ret = mmc_get_boot_part((u32 *)argp);
+            break;
+        case BIO_IOCTL_QUERY_CAP_REWRITABLE:
+            *(bool *)argp = true;
+            ret = NO_ERROR;
+            break;
+        default:
+            ret = ERR_INVALID_ARGS;
+            break;
+    }
+
+done:
+    mutex_release(&host->lock);
+    return ret;
+}
+
+static int mmc_init_mem_card_stage1(struct mmc_host *host,
+                                    struct mmc_card *card, u32 ocr)
+{
+    int err;
+
+    /*
+     * Sanity check the voltages that the card claims to
+     * support.
+     */
+    if (ocr & 0x7F)
+        ocr &= ~0x7F;
+
+    ocr = host->ocr = mmc_select_voltage(host, ocr);
+
+    /*
+     * Can we support the voltage(s) of the card(s)?
+     */
+    if (!host->ocr) {
+        err = MMC_ERR_FAILED;
+        goto out;
+    }
+
+    err = mmc_go_idle(host);
+    if (err != MMC_ERR_NONE) {
+        dprintf(CRITICAL, "[eMMC] Fail in GO_IDLE_STATE cmd\n");
+        goto out;
+    }
+
+    /* send interface condition */
+    if (mmc_card_sd(card))
+        err = mmc_send_if_cond(host, ocr);
+
+    /* host support HCS[30] */
+    ocr |= (1 << 30);
+
+    /* send operation condition */
+    if (mmc_card_sd(card)) {
+        err = mmc_send_app_op_cond_once(host, ocr, &card->ocr);
+    } else {
+        /* The extra bit indicates that we support high capacity */
+        err = mmc_send_op_cond_once(host, ocr, &card->ocr);
+    }
+
+out:
+    /* MMC_ERR_RETRY is not error */
+    return err;
+}
+
+static int mmc_init_mem_card_stage2(struct mmc_host *host,
+                                    struct mmc_card *card, bool retry_opcond)
+{
+    int err = MMC_ERR_NONE;
+    u32 ocr = host->ocr;
+
+    /* Since do not send CMD1 in BL33 stage, so just assume card capacity > 2GB */
+    if (host->skip_reinit) {
+        card->state |= MMC_STATE_HIGHCAPS;
+        card->rca = 0x1;
+        goto deselect_card;
+    }
+    /* host support HCS[30] */
+    ocr |= (1 << 30);
+
+    if (retry_opcond) {
+        /* send operation condition */
+        if (mmc_card_sd(card)) {
+            err = mmc_send_app_op_cond(host, ocr, &card->ocr);
+        } else {
+            /* The extra bit indicates that we support high capacity */
+            err = mmc_send_op_cond(host, ocr, &card->ocr);
+        }
+    }
+
+    if (err != MMC_ERR_NONE) {
+        LTRACEF("Fail in SEND_OP_COND cmd\n");
+        goto out;
+    }
+
+    /* set hcs bit if a high-capacity card */
+    card->state |= ((card->ocr >> 30) & 0x1) ? MMC_STATE_HIGHCAPS : 0;
+    /* send cid */
+    err = mmc_all_send_cid(host, card->raw_cid);
+    if (err != MMC_ERR_NONE) {
+        dprintf(CRITICAL, "[eMMC] Fail in SEND_CID cmd\n");
+        goto out;
+    }
+
+    /* assign a rca */
+    card->rca = 0x1;
+
+    /* set/send rca */
+    err = mmc_send_relative_addr(host, card, &card->rca);
+    if (err != MMC_ERR_NONE) {
+        dprintf(CRITICAL, "[eMMC] Fail in SEND_RCA cmd\n");
+        goto out;
+    }
+
+deselect_card:
+    /* de-select card for re-send CMD9 */
+    if (host->skip_reinit) {
+        err = mmc_deselect_card(host, card);
+        if (err) {
+            /* should not have error, since deselect cmd has no response */
+            dprintf(CRITICAL, "BL33 stage, failed to deselect card!\n");
+            goto out;
+        }
+    }
+    /* send csd */
+    err = mmc_read_csds(host, card);
+    if (err != MMC_ERR_NONE) {
+        dprintf(CRITICAL, "[eMMC] Fail in SEND_CSD cmd\n");
+        goto out;
+    }
+    mmc_decode_csd(card);
+
+    /* select this card */
+    err = mmc_select_card(host, card);
+    if (err != MMC_ERR_NONE) {
+        dprintf(CRITICAL, "[eMMC] Fail in select card cmd\n");
+        goto out;
+    }
+
+    if (mmc_card_sd(card)) {
+        /* set bus width */
+        mmc_set_bus_width(host, card, HOST_BUS_WIDTH_4);
+        /* compute bus speed.  usd defalut speed */
+        card->maxhz = 26000000;
+        mmc_set_clock(host, card->state, card->maxhz);
+    } else {
+
+        /* send ext csd */
+        err = mmc_read_ext_csd(host, card);
+        if (err != MMC_ERR_NONE) {
+            dprintf(CRITICAL, "[eMMC] Fail in SEND_EXT_CSD cmd\n");
+            goto out;
+        }
+
+        /*
+         * if eMMC has alreay in HS400 mode, will fail when set bus-width
+         */
+        if (host->skip_reinit && (card->ext_csd.hs_timing == 0x03)) {
+            LTRACEF("already in hs400 mode, skip tuning!\n");
+            mmc_card_set_hs200(card);
+            mmc_card_set_hs400(card);
+            mmc_set_clock(host, card->state, card->ext_csd.hs_max_dtr);
+            msdc_set_timeout(host, 100000000, 0);
+            goto card_init_done;
+        }
+
+        if (host->caps & MMC_CAP_EMMC_HS200 && host->caps & MMC_CAP_EMMC_HS400) {
+            if (card->ext_csd.hs400_support) {
+                err = mmc_select_hs200(card);
+                if (err != MMC_ERR_NONE)
+                    goto select_hs;
+                err = mmc_hs200_tuning(card);
+                if (err != MMC_ERR_NONE)
+                    goto select_hs;
+                err = mmc_select_hs400(card);
+                if (err != MMC_ERR_NONE)
+                    goto select_hs;
+                else
+                    goto card_init_done;
+            }
+        }
+
+select_hs:
+        /* activate high speed (if supported) */
+        if ((card->ext_csd.hs_max_dtr != 0) && (host->caps & MMC_CAP_MMC_HIGHSPEED)) {
+            mmc_set_clock(host, 0, host->f_min);
+            err = mmc_switch(host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1);
+            if (err == MMC_ERR_NONE) {
+                LTRACEF("[eMMC] Switched to High-Speed mode!\n");
+                mmc_card_clear_hs200(card);
+                mmc_card_clear_hs400(card);
+                mmc_card_clear_ddr(card);
+                mmc_card_set_highspeed(card);
+                mmc_set_clock(host, card->state, 50000000);
+                /* set bus width */
+                mmc_set_bus_width(host, card, HOST_BUS_WIDTH_8);
+            }
+        }
+
+card_init_done:
+        /* compute bus speed. */
+        card->maxhz = (unsigned int)-1;
+
+        if (mmc_card_highspeed(card) || mmc_card_hs400(card)) {
+            if (card->maxhz > card->ext_csd.hs_max_dtr)
+                card->maxhz = card->ext_csd.hs_max_dtr;
+        } else if (card->maxhz > card->csd.max_dtr) {
+            card->maxhz = card->csd.max_dtr;
+        }
+    }
+
+    if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
+        /* The EXT_CSD sector count is in number or 512 byte sectors. */
+        card->blklen = MMC_BLOCK_SIZE;
+        card->nblks  = card->ext_csd.sectors;
+    } else {
+        /* The CSD capacity field is in units of read_blkbits.
+         * set_capacity takes units of 512 bytes.
+         */
+        card->blklen = MMC_BLOCK_SIZE;
+        host->blklen = MMC_BLOCK_SIZE;
+        card->nblks  = card->csd.capacity << (card->csd.read_blkbits - 9);
+    }
+
+    dprintf(CRITICAL,"[eMMC/SD] Size: %d MB, Max.Speed: %d kHz, blklen(%d), nblks(%d), ro(%d)\n",
+            ((card->nblks / 1024) * card->blklen) / 1024 , card->maxhz / 1000,
+            card->blklen, card->nblks, mmc_card_readonly(card));
+
+    card->ready = 1;
+
+    LTRACEF("[eMMC/SD] Initialized\n");
+out:
+    return err;
+}
+
+static int mmc_init_card_stage1(struct mmc_host *host, struct mmc_card *card)
+{
+    int err = 0;
+    u32 ocr;
+
+    LTRACEF("[%s]: start\n", __func__);
+    memset(card, 0, sizeof(struct mmc_card));
+
+    mmc_card_set_present(card);
+    mmc_card_set_host(card, host);
+    mmc_card_set_unknown(card);
+
+    /* only consider of eMMC, don't care SD */
+    if (host->skip_reinit)
+        goto skip_reinit;
+
+    err = mmc_go_idle(host);
+    if (err != MMC_ERR_NONE) {
+        dprintf(CRITICAL, "[eMMC] Fail in GO_IDLE_STATE cmd\n");
+        goto out;
+    }
+
+    /* send interface condition */
+    if (host->host_id)
+        mmc_send_if_cond(host, host->ocr_avail);
+
+    /* query operation condition */
+
+    if (host->host_id) {
+        err = mmc_send_app_op_cond(host, 0, &ocr);
+        if (err != MMC_ERR_NONE) {
+            err = mmc_send_op_cond(host, 0, &ocr);
+            if (err != MMC_ERR_NONE) {
+                LTRACEF("Fail in MMC_CMD_SEND_OP_COND/SD_ACMD_SEND_OP_COND cmd\n");
+                goto out;
+            }
+            mmc_card_set_mmc(card);
+        } else {
+            mmc_card_set_sd(card);
+        }
+    } else {
+        err = mmc_send_op_cond(host, 0, &ocr);
+        if (err != MMC_ERR_NONE) {
+            LTRACEF("Fail in MMC_CMD_SEND_OP_COND/SD_ACMD_SEND_OP_COND cmd\n");
+            goto out;
+        }
+        mmc_card_set_mmc(card);
+    }
+
+    host->card = card;
+
+skip_reinit:
+    if (host->skip_reinit)
+        mmc_card_set_mmc(card);
+    else
+        err = mmc_init_mem_card_stage1(host, card, ocr);
+
+out:
+    return err;
+}
+
+static int mmc_init_card_stage2(struct mmc_host *host, struct mmc_card *card,
+                                bool retry_opcond)
+{
+    int err;
+
+    err = mmc_init_mem_card_stage2(host, card, retry_opcond);
+    if (err) {
+        dprintf(CRITICAL, "[%s]: failed, err=%d\n", __func__, err);
+        return err;
+    }
+    host->card = card;
+    LTRACEF("[%s]: finish successfully\n",__func__);
+    return 0;
+}
+
+static inline int mmc_init_host(struct mmc_host *host)
+{
+    mutex_init(&host->lock);
+    return msdc_init(host);
+}
+
+static int mmc_bio_ops(const void *name, const int part_id, const int nblks,
+                        struct mmc_host *host, struct mmc_card *card)
+{
+    mmc_dev_t *dev;
+    bio_erase_geometry_info_t *geometry;
+
+    dev = malloc(sizeof(mmc_dev_t));
+    /* malloc fail */
+    ASSERT(dev);
+    /* construct the block device */
+    memset(dev, 0, sizeof(mmc_dev_t));
+
+    geometry = (bio_erase_geometry_info_t *)malloc(sizeof(bio_erase_geometry_info_t));
+    if (!geometry) {
+        dprintf(CRITICAL, "%s: no enough memory for geometry\n", __func__);
+        free(dev);
+        return -1;
+    }
+
+    /* setup partition id*/
+    dev->part_id = part_id;
+    /* setup host */
+    dev->host = host;
+    /* setup card */
+    dev->card = card;
+
+    geometry->start = 0;
+    geometry->size = card->nblks * card->blklen;
+    geometry->erase_size = 512;
+    geometry->erase_shift = log2_uint(geometry->erase_size);
+    /* bio block device register */
+    bio_initialize_bdev(&dev->bdev, name,
+                        card->blklen, nblks,
+                        1, geometry, BIO_FLAGS_NONE);
+    /* override our block device hooks */
+    if (part_id == EXT_CSD_PART_CFG_RPMB_PART) {
+        dev->bdev.read_block = mmc_rpmb_dummy_read;
+        dev->bdev.write_block = mmc_rpmb_dummy_write;
+        dev->bdev.erase = mmc_rpmb_dummy_erase;
+        dev->bdev.ioctl = mmc_rpmb_ioctl;
+    } else {
+        dev->bdev.read_block = mmc_block_read;
+        dev->bdev.write_block = mmc_block_write;
+        dev->bdev.erase = mmc_wrap_erase;
+        if (part_id == EXT_CSD_PART_CFG_DEFT_PART)
+            dev->bdev.ioctl = mmc_ioctl;
+    }
+    bio_register_device(&dev->bdev);
+    return (partition_publish(dev->bdev.name, 0x0) < 0) ? -1 : 0;
+}
+
+struct mmc_card *emmc_init_stage1(bool *retry_opcond, bool skip_reinit)
+{
+    int err = MMC_ERR_NONE;
+    struct mmc_host *host;
+    struct mmc_card *card;
+
+    host = &msdc_host0;
+    /* construct the block device */
+    memset(host, 0, sizeof(struct mmc_host));
+    host->host_id = 0;
+
+    host->skip_reinit = skip_reinit;
+
+    card = &emmc_card;
+    /* construct the block device */
+    memset(card, 0, sizeof(struct mmc_card));
+
+    err = mmc_init_host(host);
+
+    if (err == MMC_ERR_NONE)
+        err = mmc_init_card_stage1(host, card);
+
+    if (err && err != MMC_ERR_RETRY) {
+        dprintf(CRITICAL, "failed in %s \n", __func__);
+        return NULL;
+    } else if (err == MMC_ERR_RETRY) {
+        *retry_opcond = true;
+    } else {
+        *retry_opcond = false;
+    }
+
+    return card;
+}
+
+int emmc_init_stage2(struct mmc_card *card, bool retry_opcond, bool skip_reinit)
+{
+    int err = MMC_ERR_NONE;
+    struct mmc_host *host;
+    int boot_part_nblks = 0;
+    int rpmb_part_nblks = 0;
+
+    host = card->host;
+
+    host->skip_reinit = skip_reinit;
+
+    err = mmc_init_card_stage2(host, card, retry_opcond);
+    /* mmc init fail */
+    ASSERT(err == MMC_ERR_NONE);
+
+    err = emmc_boot_prepare(card);
+    ASSERT(err == MMC_ERR_NONE);
+
+    err = mmc_bio_ops("mmc0", EXT_CSD_PART_CFG_DEFT_PART, card->nblks, host, card);
+    if (err)
+        goto error;
+    boot_part_nblks = card->ext_csd.boot_part_sz/card->blklen;
+    err = mmc_bio_ops("mmc0boot0", EXT_CSD_PART_CFG_BOOT_PART_1, boot_part_nblks,
+                host, card);
+    if (err)
+        goto error;
+    err = mmc_bio_ops("mmc0boot1", EXT_CSD_PART_CFG_BOOT_PART_2, boot_part_nblks,
+                host, card);
+    if (err)
+        goto error;
+    rpmb_part_nblks = card->ext_csd.rpmb_sz/card->blklen;
+    err = mmc_bio_ops("mmc0rpmb", EXT_CSD_PART_CFG_RPMB_PART, rpmb_part_nblks,
+                host, card);
+
+error:
+    return err;
+}
+
+int sdmmc_init(u8 host_id, bool skip_reinit)
+{
+    int err = MMC_ERR_NONE;
+    struct mmc_host *host;
+    struct mmc_card *card;
+    bool retry_opcond;
+
+    printf("%s enter\n", __func__);
+
+    host = malloc(sizeof(struct mmc_host));
+    /* malloc fail */
+    if (!host) {
+        LTRACEF("Failed to malloc host!\n");
+        err = -ENOMEM;
+        goto end;
+    }
+    /* construct the block device */
+    memset(host, 0, sizeof(struct mmc_host));
+    host->host_id = host_id;
+
+    host->skip_reinit = skip_reinit;
+
+    card = malloc(sizeof(struct mmc_card));
+    /* malloc fail */
+    if (!card) {
+        LTRACEF("Failed to malloc card!\n");
+        free(host);
+        err = -ENOMEM;
+        goto end;
+    }
+    /* construct the block device */
+    memset(card, 0, sizeof(struct mmc_card));
+
+    err = mmc_init_host(host);
+
+    if (err == MMC_ERR_NONE)
+        err = mmc_init_card_stage1(host, card);
+    /* mmc init fail */
+    if (err && err != MMC_ERR_RETRY) {
+        LTRACEF("mmc_init_card fail!\n");
+        free(host);
+        free(card);
+        goto end;
+    } else if (err == MMC_ERR_RETRY) {
+        retry_opcond = true;
+    } else {
+        retry_opcond = false;
+    }
+
+    err = mmc_init_card_stage2(host, card, retry_opcond);
+    if (err != MMC_ERR_NONE) {
+        LTRACEF("mmc_init_card fail!\n");
+        free(host);
+        free(card);
+        goto end;
+    }
+    err = mmc_bio_ops("sdmmc1", EXT_CSD_PART_CFG_DEFT_PART, card->nblks, host, card);
+
+end:
+    return err;
+}
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/mmc/mmc_rpmb.c b/src/bsp/lk/platform/mediatek/mt2712/drivers/mmc/mmc_rpmb.c
new file mode 100644
index 0000000..1608510
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/mmc/mmc_rpmb.c
@@ -0,0 +1,429 @@
+/*
+ * Copyright (c) 2017 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <lib/bio.h>
+#include <platform/mmc_ioctl.h>
+#include <platform/mmc_rpmb.h>
+#include <platform/mtk_bio_ioctl.h>
+#include <platform/mtk_trng.h>
+#include <rpmb_mac.h>
+#include <debug.h>
+#include <trace.h>
+
+#define LOCAL_TRACE 0
+
+static void reverse_endian(void *data, size_t size)
+{
+    unsigned int i;
+    char tmp;
+    char *swp = (char *)data;
+
+    for (i = 0 ; i< (size/2); ++i) {
+        tmp = swp[i];
+        swp[i] = swp[size-1-i];
+        swp[size-1-i] = tmp;
+    }
+}
+
+int mmc_rpmb_set_key(u8 *key)
+{
+    int ret;
+    bdev_t *rpdev;
+    struct mmc_ioc_cmd *idata;
+    unsigned char *rpmb_pkt;
+    unsigned char nonce[RPMB_SZ_NONCE];
+    unsigned char back_nonce[RPMB_SZ_NONCE];
+    unsigned int i;
+
+    idata = malloc(sizeof(struct mmc_ioc_cmd));
+    if (idata == NULL) {
+        return -2;
+    }
+    rpmb_pkt = malloc(512);
+    if (rpmb_pkt == NULL) {
+        free(idata);
+        return -2;
+    }
+
+    memset(idata, 0, sizeof(struct mmc_ioc_cmd));
+    memset(rpmb_pkt, 0, 512);
+
+    rpdev = bio_open("mmc0rpmb");
+    if (rpdev == NULL) {
+        free(rpmb_pkt);
+        free(idata);
+        return -3;
+    }
+
+    /* get wc for status */
+    rpmb_pkt[0] = 2;
+    if (trng_drv_get_random_data(nonce, RPMB_SZ_NONCE) != RPMB_SZ_NONCE)
+        return -4;
+
+    idata->flags = RESP_R1;
+    idata->opcode = MMC_CMD_WRITE_MULTIPLE_BLOCK;
+    idata->blksz = 512;
+    idata->blocks = 1;
+    idata->write_flag = 1;
+    reverse_endian(rpmb_pkt, 512);
+    memcpy(rpmb_pkt + RPMB_NONCE_BEG, nonce, RPMB_SZ_NONCE);
+    mmc_ioc_cmd_set_data((*idata), rpmb_pkt);
+
+    ret = bio_ioctl(rpdev, BIO_IOCTL_MMC_IOC_CMD, idata);
+    if (ret != 0)
+        goto rpmb_end;
+
+    memset(idata, 0, sizeof(struct mmc_ioc_cmd));
+    memset(rpmb_pkt, 0, 512);
+    idata->flags = RESP_R1;
+    idata->opcode = MMC_CMD_READ_MULTIPLE_BLOCK;
+    idata->blksz = 512;
+    idata->blocks = 1;
+    idata->write_flag = 0;
+    mmc_ioc_cmd_set_data((*idata), rpmb_pkt);
+
+    ret = bio_ioctl(rpdev, BIO_IOCTL_MMC_IOC_CMD, idata);
+    if (ret != 0)
+        goto rpmb_end;
+
+    memcpy(back_nonce, &rpmb_pkt[RPMB_NONCE_BEG], RPMB_SZ_NONCE);
+
+    reverse_endian(rpmb_pkt, 512);
+    /* check result */
+    ret = *(unsigned short *)&rpmb_pkt[2];
+    if (ret != 7) { /* 7 means not programmed yet */
+        for (i = 0; i < RPMB_SZ_NONCE; i++) {
+            if (nonce[i] ^ back_nonce[i]) {
+                dprintf(CRITICAL, "nonce mismatch!\n");
+                ret = -5;
+                goto rpmb_end;
+            }
+        }
+        /* key has already programmed */
+        LTRACEF("ret=%d, rpmb key has already programmed.\n", ret);
+        goto rpmb_end;
+    }
+    LTRACEF("ret=%d, key not programmed yet\n", ret);
+
+    memset(idata, 0, sizeof(struct mmc_ioc_cmd));
+    memset(rpmb_pkt, 0, 512);
+
+    rpmb_pkt[0] = 1;
+
+    idata->flags = RESP_R1;
+    idata->opcode = MMC_CMD_WRITE_MULTIPLE_BLOCK;
+    idata->blksz = 512;
+    idata->blocks = 1;
+    idata->write_flag = 1 << 31;
+    reverse_endian(rpmb_pkt, 512);
+    memcpy(&rpmb_pkt[RPMB_MAC_BEG], key, 32);
+    mmc_ioc_cmd_set_data((*idata), rpmb_pkt);
+
+    ret = bio_ioctl(rpdev, BIO_IOCTL_MMC_IOC_CMD, idata);
+    if (ret != 0)
+        goto rpmb_end;
+
+    memset(idata, 0, sizeof(struct mmc_ioc_cmd));
+    memset(rpmb_pkt, 0, 512);
+
+    rpmb_pkt[0] = 5;
+
+    idata->flags = RESP_R1;
+    idata->opcode = MMC_CMD_WRITE_MULTIPLE_BLOCK;
+    idata->blksz = 512;
+    idata->blocks = 1;
+    idata->write_flag = 1;
+    reverse_endian(rpmb_pkt, 512);
+    mmc_ioc_cmd_set_data((*idata), rpmb_pkt);
+
+    ret = bio_ioctl(rpdev, BIO_IOCTL_MMC_IOC_CMD, idata);
+    if (ret != 0)
+        goto rpmb_end;
+
+
+    memset(idata, 0, sizeof(struct mmc_ioc_cmd));
+    memset(rpmb_pkt, 0, 512);
+
+    idata->flags = RESP_R1;
+    idata->opcode = MMC_CMD_READ_MULTIPLE_BLOCK;
+    idata->blksz = 512;
+    idata->blocks = 1;
+    idata->write_flag = 0;
+    mmc_ioc_cmd_set_data((*idata), rpmb_pkt);
+
+    ret = bio_ioctl(rpdev, BIO_IOCTL_MMC_IOC_CMD, idata);
+    if (ret != 0)
+        goto rpmb_end;
+
+    reverse_endian(rpmb_pkt, 512);
+
+    ret = ((unsigned short *)&rpmb_pkt)[2];
+
+rpmb_end:
+    free(rpmb_pkt);
+    free(idata);
+    bio_close(rpdev);
+    return ret;
+}
+
+int mmc_rpmb_block_read(int blknr, unsigned char blk[256])
+{
+    int ret;
+    bdev_t *rpdev;
+    struct mmc_ioc_cmd *idata;
+    unsigned char *rpmb_pkt;
+    unsigned char mac[RPMB_SZ_MAC];
+    unsigned char nonce[RPMB_SZ_NONCE];
+    unsigned int mac_sz = RPMB_SZ_MAC;
+    int i;
+
+    idata = malloc(sizeof(struct mmc_ioc_cmd));
+    if (idata == NULL)
+        return -2;
+    rpmb_pkt = malloc(512);
+    if (rpmb_pkt == NULL) {
+        free(idata);
+        return -2;
+    }
+
+    memset(idata, 0, sizeof(struct mmc_ioc_cmd));
+    memset(rpmb_pkt, 0, 512);
+
+    rpdev = bio_open("mmc0rpmb");
+    if (rpdev == NULL) {
+        free(rpmb_pkt);
+        free(idata);
+        return -3;
+    }
+
+    /* read */
+    rpmb_pkt[0] = 4;
+    rpmb_pkt[6] = blknr;
+    if (trng_drv_get_random_data(nonce, RPMB_SZ_NONCE) != RPMB_SZ_NONCE)
+        return -4;
+
+    idata->flags = RESP_R1;
+    idata->opcode = MMC_CMD_WRITE_MULTIPLE_BLOCK;
+    idata->blksz = 512;
+    idata->blocks = 1;
+    idata->write_flag = 1;
+    reverse_endian(rpmb_pkt, 512);
+    memcpy(rpmb_pkt + RPMB_NONCE_BEG, nonce, RPMB_SZ_NONCE);
+    mmc_ioc_cmd_set_data((*idata), rpmb_pkt);
+
+    ret = bio_ioctl(rpdev, BIO_IOCTL_MMC_IOC_CMD, idata);
+    if (ret != 0)
+        goto rpmb_end;
+    spin(1000);
+    memset(idata, 0, sizeof(struct mmc_ioc_cmd));
+    memset(rpmb_pkt, 0, 512);
+
+    idata->flags = RESP_R1;
+    idata->opcode = MMC_CMD_READ_MULTIPLE_BLOCK;
+    idata->blksz = 512;
+    idata->blocks = 1;
+    idata->write_flag = 0;
+    reverse_endian(rpmb_pkt, 512);
+    mmc_ioc_cmd_set_data((*idata), rpmb_pkt);
+
+    ret = bio_ioctl(rpdev, BIO_IOCTL_MMC_IOC_CMD, idata);
+    if (ret != 0)
+        goto rpmb_end;
+
+    ret = *(unsigned short *)&rpmb_pkt[RPMB_RES_BEG];
+    reverse_endian(&ret, sizeof(short));
+    if (ret != 0)
+        goto rpmb_end;
+
+    rpmb_hmac_init(rpmb_pkt + RPMB_DATA_BEG, 512 - RPMB_DATA_BEG);
+    rpmb_hmac_done(mac, &mac_sz);
+    ret = 0;
+    for (i = 0; i < RPMB_SZ_NONCE; i++)
+        ret |= nonce[i] ^ rpmb_pkt[RPMB_NONCE_BEG + i];
+
+    if (ret != 0) {
+        ret = -5;
+        goto rpmb_end;
+    }
+    for (i = 0; i < RPMB_SZ_MAC; i++)
+        ret |= mac[i] ^ rpmb_pkt[RPMB_MAC_BEG + i];
+
+    if (ret != 0) {
+        ret = -1;
+        goto rpmb_end;
+    }
+    memcpy(blk, rpmb_pkt + RPMB_DATA_BEG, RPMB_SZ_DATA);
+    reverse_endian(blk, RPMB_SZ_DATA);
+
+rpmb_end:
+    free(rpmb_pkt);
+    free(idata);
+    bio_close(rpdev);
+    return ret;
+}
+
+int mmc_rpmb_block_write(int blknr, unsigned char blk[256])
+{
+    int ret;
+    bdev_t *rpdev;
+    struct mmc_ioc_cmd *idata;
+    unsigned char *rpmb_pkt;
+    unsigned int wc, mac_sz = RPMB_SZ_MAC;
+    unsigned char nonce[RPMB_SZ_NONCE];
+    unsigned int i;
+
+    idata = malloc(sizeof(struct mmc_ioc_cmd));
+    if (idata == NULL) {
+        return -2;
+    }
+    rpmb_pkt = malloc(512);
+    if (rpmb_pkt == NULL) {
+        free(idata);
+        return -2;
+    }
+
+    memset(idata, 0, sizeof(struct mmc_ioc_cmd));
+    memset(rpmb_pkt, 0, 512);
+
+    rpdev = bio_open("mmc0rpmb");
+    if (rpdev == NULL) {
+        free(rpmb_pkt);
+        free(idata);
+        return -3;
+    }
+
+    /* get wc */
+    rpmb_pkt[0] = 2;
+    if (trng_drv_get_random_data(nonce, RPMB_SZ_NONCE) != RPMB_SZ_NONCE)
+        return -4;
+
+    idata->flags = RESP_R1;
+    idata->opcode = MMC_CMD_WRITE_MULTIPLE_BLOCK;
+    idata->blksz = 512;
+    idata->blocks = 1;
+    idata->write_flag = 1;
+    reverse_endian(rpmb_pkt, 512);
+    memcpy(rpmb_pkt + RPMB_NONCE_BEG, nonce, RPMB_SZ_NONCE);
+    mmc_ioc_cmd_set_data((*idata), rpmb_pkt);
+
+    ret = bio_ioctl(rpdev, BIO_IOCTL_MMC_IOC_CMD, idata);
+    if (ret != 0)
+        goto rpmb_end;
+
+    memset(idata, 0, sizeof(struct mmc_ioc_cmd));
+    memset(rpmb_pkt, 0, 512);
+    idata->flags = RESP_R1;
+    idata->opcode = MMC_CMD_READ_MULTIPLE_BLOCK;
+    idata->blksz = 512;
+    idata->blocks = 1;
+    idata->write_flag = 0;
+    mmc_ioc_cmd_set_data((*idata), rpmb_pkt);
+
+    ret = bio_ioctl(rpdev, BIO_IOCTL_MMC_IOC_CMD, idata);
+    if (ret != 0)
+        goto rpmb_end;
+
+    ret = 0;
+    for (i = 0; i < RPMB_SZ_NONCE; i++)
+        ret |= nonce[i] ^ rpmb_pkt[RPMB_NONCE_BEG + i];
+
+    if (ret != 0) {
+        ret = -5;
+        goto rpmb_end;
+    }
+    reverse_endian(rpmb_pkt, 512);
+    /* check result */
+    ret = *(unsigned short *)&rpmb_pkt[2];
+    if (ret != 0) /* get success */
+        goto rpmb_end;
+    wc = *(unsigned int *)&rpmb_pkt[8];
+
+    memset(idata, 0, sizeof(struct mmc_ioc_cmd));
+    memset(rpmb_pkt, 0, 512);
+    /* do write */
+    rpmb_pkt[0] = 0x3;
+    rpmb_pkt[4] = 1;
+    rpmb_pkt[6] = blknr;
+    *(unsigned int *)&rpmb_pkt[8] = wc;
+    memcpy(rpmb_pkt + 28, blk, RPMB_SZ_DATA);
+
+    idata->flags = RESP_R1;
+    idata->opcode = MMC_CMD_WRITE_MULTIPLE_BLOCK;
+    idata->blksz = 512;
+    idata->blocks = 1;
+    idata->write_flag = 1<<31;
+    reverse_endian(rpmb_pkt, 512);
+    rpmb_hmac_init(rpmb_pkt + RPMB_DATA_BEG, 512 - RPMB_DATA_BEG);
+    rpmb_hmac_done(rpmb_pkt + RPMB_MAC_BEG, &mac_sz);
+    mmc_ioc_cmd_set_data((*idata), rpmb_pkt);
+
+    ret = bio_ioctl(rpdev, BIO_IOCTL_MMC_IOC_CMD, idata);
+    if (ret != 0)
+        goto rpmb_end;
+    spin(1000);
+
+    memset(idata, 0, sizeof(struct mmc_ioc_cmd));
+    memset(rpmb_pkt, 0, 512);
+    /* request to read result back */
+    rpmb_pkt[0] = 0x5;
+
+    idata->flags = RESP_R1;
+    idata->opcode = MMC_CMD_WRITE_MULTIPLE_BLOCK;
+    idata->blksz = 512;
+    idata->blocks = 1;
+    idata->write_flag = 1;
+    reverse_endian(rpmb_pkt, 512);
+    mmc_ioc_cmd_set_data((*idata), rpmb_pkt);
+
+    ret = bio_ioctl(rpdev, BIO_IOCTL_MMC_IOC_CMD, idata);
+    if (ret != 0)
+        goto rpmb_end;
+
+    spin(1000);
+    memset(idata, 0, sizeof(struct mmc_ioc_cmd));
+    memset(rpmb_pkt, 0, 512);
+    /* read result back */
+    idata->flags = RESP_R1;
+    idata->opcode = MMC_CMD_READ_MULTIPLE_BLOCK;
+    idata->blksz = 512;
+    idata->blocks = 1;
+    idata->write_flag = 0;
+    reverse_endian(rpmb_pkt, 512);
+    mmc_ioc_cmd_set_data((*idata), rpmb_pkt);
+
+    ret = bio_ioctl(rpdev, BIO_IOCTL_MMC_IOC_CMD, idata);
+    if (ret != 0)
+        goto rpmb_end;
+
+    reverse_endian(rpmb_pkt, 512);
+    /* get result */
+    ret = *(unsigned short *)&rpmb_pkt[2];
+
+rpmb_end:
+    free(rpmb_pkt);
+    free(idata);
+    bio_close(rpdev);
+    return ret;
+}
+
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/mmc/msdc.c b/src/bsp/lk/platform/mediatek/mt2712/drivers/mmc/msdc.c
new file mode 100644
index 0000000..1618abe
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/mmc/msdc.c
@@ -0,0 +1,2096 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#define MSDC_DEBUG_KICKOFF
+
+#include <platform/msdc.h>
+#include <platform/pll.h>
+#include <platform/mmc_core.h>
+#include <kernel/event.h>
+#include <kernel/vm.h>
+#include <platform/interrupts.h>
+#include <platform/mt_irq.h>
+#include <string.h>
+#include <assert.h>
+#include <trace.h>
+
+#define LOCAL_TRACE        0
+
+#define CMD_RETRIES        (5)
+#define CMD_TIMEOUT        (100) /* 100ms */
+
+#define PERI_MSDC_SRCSEL   (0xc100000c)
+
+/* Tuning Parameter */
+#define DEFAULT_DEBOUNCE   (8)  /* 8 cycles */
+#define DEFAULT_DTOC       (40) /* data timeout counter. 65536x40 sclk. */
+#define DEFAULT_WDOD       (0)  /* write data output delay. no delay. */
+#define DEFAULT_BSYDLY     (8)  /* card busy delay. 8 extend sclk */
+
+#define MSDC_USE_PATCH_BIT2_TURNING_WITH_ASYNC 1
+/* Declarations */
+static int msdc_send_cmd(struct mmc_host *host, struct mmc_command *cmd);
+static int msdc_wait_cmd_done(struct mmc_host *host, struct mmc_command *cmd);
+static int msdc_tune_cmdrsp(struct mmc_host *host, struct mmc_command *cmd);
+
+typedef struct {
+    int    autocmd;
+    int    rdsmpl;
+    int    wdsmpl;
+    int    rsmpl;
+    int    start_bit;
+} msdc_priv_t;
+
+static int msdc_rsp[] = {
+    0,  /* RESP_NONE */
+    1,  /* RESP_R1 */
+    2,  /* RESP_R2 */
+    3,  /* RESP_R3 */
+    4,  /* RESP_R4 */
+    1,  /* RESP_R5 */
+    1,  /* RESP_R6 */
+    1,  /* RESP_R7 */
+    7,  /* RESP_R1b */
+};
+
+struct msdc_cust {
+    unsigned char  clk_src;           /* host clock source             */
+    unsigned char  hclk_src;           /* host clock source             */
+    unsigned char  cmd_edge;          /* command latch edge            */
+    unsigned char  data_edge;         /* data latch edge               */
+#define MSDC_SMPL_RISING        (0)
+#define MSDC_SMPL_FALLING       (1)
+#define MSDC_SMPL_SEPERATE      (2)
+    unsigned char  clk_drv;           /* clock pad driving             */
+    unsigned char  cmd_drv;           /* command pad driving           */
+    unsigned char  dat_drv;           /* data pad driving              */
+    unsigned char  rst_drv;           /* reset pin pad driving         */
+    unsigned char  ds_drv;            /* ds pad driving                */
+    unsigned char  data_pins;         /* data pins                     */
+    unsigned int   data_offset;       /* data address offset           */
+    unsigned int   flags;             /* hardware capability flags     */
+#define MSDC_CD_PIN_EN      (1 << 0)  /* card detection pin is wired   */
+#define MSDC_WP_PIN_EN      (1 << 1)  /* write protection pin is wired */
+#define MSDC_RST_PIN_EN     (1 << 2)  /* emmc reset pin is wired       */
+#define MSDC_SDIO_IRQ       (1 << 3)  /* use internal sdio irq (bus)   */
+#define MSDC_EXT_SDIO_IRQ   (1 << 4)  /* use external sdio irq         */
+#define MSDC_REMOVABLE      (1 << 5)  /* removable slot                */
+#define MSDC_SYS_SUSPEND    (1 << 6)  /* suspended by system           */
+#define MSDC_HIGHSPEED      (1 << 7)  /* high-speed mode support       */
+#define MSDC_UHS1           (1 << 8)  /* uhs-1 mode support            */
+#define MSDC_DDR            (1 << 9)  /* ddr mode support              */
+#define MSDC_HS200          (1 << 10) /* hs200 mode support(eMMC4.5)   */
+#define MSDC_HS400          (1 << 11) /* hs200 mode support(eMMC5.0)   */
+} msdc_cap[2] = {
+    {
+        MSDC50_CLKSRC_DEFAULT, /* host clock source          */
+        MSDC50_CLKSRC4HCLK_273MHZ, /* host clock source          */
+        MSDC_SMPL_RISING,   /* command latch edge            */
+        MSDC_SMPL_RISING,   /* data latch edge               */
+        MSDC_DRVN_GEAR2,    /* clock pad driving             */
+        MSDC_DRVN_GEAR2,    /* command pad driving           */
+        MSDC_DRVN_GEAR2,    /* data pad driving              */
+        MSDC_DRVN_GEAR2,    /* rst pad driving               */
+        MSDC_DRVN_GEAR2,    /* ds pad driving                */
+        8,                  /* data pins                     */
+        0,                  /* data address offset           */
+        MSDC_HIGHSPEED | MSDC_HS200 | MSDC_HS400
+    },
+
+    {
+        MSDC50_CLKSRC_DEFAULT, /* host clock source          */
+        MSDC50_CLKSRC4HCLK_273MHZ, /* host clock source          */
+        MSDC_SMPL_RISING,   /* command latch edge            */
+        MSDC_SMPL_RISING,   /* data latch edge               */
+        MSDC_DRVN_GEAR2,    /* clock pad driving             */
+        MSDC_DRVN_GEAR2,    /* command pad driving           */
+        MSDC_DRVN_GEAR2,    /* data pad driving              */
+        MSDC_DRVN_GEAR2,    /* rst pad driving               */
+        MSDC_DRVN_GEAR2,    /* ds pad driving                */
+        4,                  /* data pins                     */
+        0,                  /* data address offset           */
+        MSDC_HIGHSPEED
+    },
+};
+
+static event_t msdc_int_event;
+static u32 g_int_status = 0;
+static msdc_priv_t msdc_priv;
+static u32 hclks_msdc30[] = { 26000000, 208000000, 200000000, 156000000,
+                              182000000, 156000000, 178280000
+                            };
+
+/* add function for MSDC_PAD_CTL handle */
+#ifndef FPGA_PLATFORM
+static void msdc_set_smt(struct mmc_host *host, int smt)
+{
+    MSDC_SET_FIELD(MSDC_CTRL0, MSDC0_CLK_SMT, smt);
+    MSDC_SET_FIELD(MSDC_CTRL4, MSDC0_DS_SMT, smt);
+    MSDC_SET_FIELD(MSDC_CTRL1, MSDC0_CMD_SMT, smt);
+    MSDC_SET_FIELD(MSDC_CTRL2, MSDC0_DAT_SMT, smt);
+}
+
+/* pull up means that host driver the line to HIGH
+ * pull down means that host driver the line to LOW */
+static void msdc_pin_set(struct mmc_host *host, bool pull_down, MSDC_PIN_STATE mode)
+{
+    /* driver CLK/DAT pin */
+    ASSERT(host);
+    ASSERT(mode < MSDC_PST_MAX);
+
+    MSDC_SET_FIELD(MSDC_CTRL0, MSDC0_CLK_PUPD, 1); /* clock is pull-down */
+    MSDC_SET_FIELD(MSDC_CTRL0, MSDC0_CLK_R1R0, 2); /* clock is pull-down 50K */
+
+    MSDC_SET_FIELD(MSDC_CTRL4, MSDC0_DS_PUPD, 1); /* DS is pull-down */
+    MSDC_SET_FIELD(MSDC_CTRL4, MSDC0_DS_R1R0, 2); /* DS is pull-down 50K */
+
+    MSDC_SET_FIELD(MSDC_CTRL1, MSDC0_CMD_PUPD, pull_down);
+    MSDC_SET_FIELD(MSDC_CTRL1, MSDC0_CMD_R1R0, mode);
+
+    MSDC_SET_FIELD(MSDC_CTRL2, MSDC0_DAT_PUPD, pull_down);
+    MSDC_SET_FIELD(MSDC_CTRL2, MSDC0_DAT_R1R0, mode);
+}
+
+/* host can modify from 0-7 */
+static void msdc_set_driving(struct mmc_host *host, struct msdc_cust *msdc_cap)
+{
+    ASSERT(host);
+    ASSERT(msdc_cap);
+
+    if (host && msdc_cap) {
+        MSDC_SET_FIELD(MSDC_CTRL0, MSDC0_DRV_CLK_MASK, msdc_cap->clk_drv);
+        MSDC_SET_FIELD(MSDC_CTRL1, MSDC0_DRV_CMD_MASK, msdc_cap->cmd_drv);
+        MSDC_SET_FIELD(MSDC_CTRL2, MSDC0_DRV_DAT_MASK, msdc_cap->dat_drv);
+        MSDC_SET_FIELD(MSDC_CTRL4, MSDC0_DRV_DS_MASK, msdc_cap->ds_drv);
+    }
+}
+#endif
+
+#ifndef FPGA_PLATFORM /* don't power on/off device and use power-on default volt */
+/* MT2712EVB, GPIO67 to control SD VCCQ, output H --> 3.3V, output L --> 1.8V */
+/* set to 3.3V */
+static void sd_card_vccq_on(void)
+{
+    MSDC_SET_FIELD(GPIO_MODE14, 0x7 << 6, 0);
+    MSDC_SET_BIT32(GPIO_DIR5, 0x1 << 3);
+    MSDC_SET_BIT32(GPIO_DOUT5, 0x1 << 3);
+}
+
+static int pmic_config_interface(int a, int b, int c, int d)
+{
+    return 0;
+}
+
+static void msdc_set_card_pwr(struct mmc_host *host, int on)
+{
+    unsigned int ret;
+
+    ret = pmic_config_interface(0xAB,0x7,0x7,4); /* VMCH=3.3V */
+
+    if (ret == 0) {
+        if (on) {
+            ret = pmic_config_interface(0xAB,0x1,0x1,0); /* VMCH_EN=1 */
+        } else {
+            ret = pmic_config_interface(0xAB,0x0,0x1,0); /* VMCH_EN=0 */
+        }
+    }
+
+    if (ret != 0) {
+        dprintf(CRITICAL, "PMIC: Set MSDC Host Power Fail\n");
+    } else {
+        spin(3000);
+    }
+}
+
+static void msdc_set_host_pwr(struct mmc_host *host, int on)
+{
+    unsigned int ret;
+
+    ret = pmic_config_interface(0xA7,0x7,0x7,4); /* VMC=3.3V */
+
+    if (ret == 0) {
+        if (on) {
+            ret = pmic_config_interface(0xA7,0x1,0x1,0); /* VMC_EN=1 */
+        } else {
+            ret = pmic_config_interface(0xA7,0x0,0x1,0); /* VMC_EN=0 */
+        }
+    }
+
+    if (ret != 0) {
+        dprintf(CRITICAL, "PMIC: Set MSDC Card Power Fail\n");
+    }
+}
+#else
+#define PWR_GPIO            (0x10001E84)
+#define PWR_GPIO_EO         (0x10001E88)
+
+#define PWR_MASK_EN         (0x1 << 8)
+#define PWR_MASK_VOL_18     (0x1 << 9)
+#define PWR_MASK_VOL_33     (0x1 << 10)
+#define PWR_MASK_L4         (0x1 << 11)
+#define PWR_MSDC_DIR        (PWR_MASK_EN | PWR_MASK_VOL_18 | PWR_MASK_VOL_33 | PWR_MASK_L4)
+
+#define MSDC0_PWR_MASK_EN         (0x1 << 12)
+#define MSDC0_PWR_MASK_VOL_18     (0x1 << 13)
+#define MSDC0_PWR_MASK_VOL_33     (0x1 << 14)
+#define MSDC0_PWR_MASK_L4         (0x1 << 15)
+#define MSDC0_PWR_MSDC_DIR        (MSDC0_PWR_MASK_EN | MSDC0_PWR_MASK_VOL_18 | MSDC0_PWR_MASK_VOL_33 | MSDC0_PWR_MASK_L4)
+
+static void msdc_clr_gpio(u32 bits)
+{
+    u32 l_val = 0;
+
+    switch (bits) {
+        case MSDC0_PWR_MASK_EN:
+            /* check for set before */
+            MSDC_SET_FIELD(PWR_GPIO,    (MSDC0_PWR_MASK_EN),0);
+            MSDC_SET_FIELD(PWR_GPIO_EO, (MSDC0_PWR_MASK_EN),0);
+            break;
+        case MSDC0_PWR_MASK_VOL_18:
+            /* check for set before */
+            MSDC_SET_FIELD(PWR_GPIO,    (MSDC0_PWR_MASK_VOL_18|MSDC0_PWR_MASK_VOL_33), 0);
+            MSDC_SET_FIELD(PWR_GPIO_EO, (MSDC0_PWR_MASK_VOL_18|MSDC0_PWR_MASK_VOL_33), 0);
+            break;
+        case MSDC0_PWR_MASK_VOL_33:
+            /* check for set before */
+            MSDC_SET_FIELD(PWR_GPIO,    (MSDC0_PWR_MASK_VOL_18|MSDC0_PWR_MASK_VOL_33), 0);
+            MSDC_SET_FIELD(PWR_GPIO_EO, (MSDC0_PWR_MASK_VOL_18|MSDC0_PWR_MASK_VOL_33), 0);
+            break;
+        case MSDC0_PWR_MASK_L4:
+            /* check for set before */
+            MSDC_SET_FIELD(PWR_GPIO,    MSDC0_PWR_MASK_L4, 0);
+            MSDC_SET_FIELD(PWR_GPIO_EO, MSDC0_PWR_MASK_L4, 0);
+            break;
+        case PWR_MASK_EN:
+            /* check for set before */
+            MSDC_SET_FIELD(PWR_GPIO,    PWR_MASK_EN,0);
+            MSDC_SET_FIELD(PWR_GPIO_EO, PWR_MASK_EN,0);
+            break;
+        case PWR_MASK_VOL_18:
+            /* check for set before */
+            MSDC_SET_FIELD(PWR_GPIO,    (PWR_MASK_VOL_18|PWR_MASK_VOL_33), 0);
+            MSDC_SET_FIELD(PWR_GPIO_EO, (PWR_MASK_VOL_18|PWR_MASK_VOL_33), 0);
+            break;
+        case PWR_MASK_VOL_33:
+            /* check for set before */
+            MSDC_SET_FIELD(PWR_GPIO,    (PWR_MASK_VOL_18|PWR_MASK_VOL_33), 0);
+            MSDC_SET_FIELD(PWR_GPIO_EO, (PWR_MASK_VOL_18|PWR_MASK_VOL_33), 0);
+            break;
+        case PWR_MASK_L4:
+            /* check for set before */
+            MSDC_SET_FIELD(PWR_GPIO,    PWR_MASK_L4, 0);
+            MSDC_SET_FIELD(PWR_GPIO_EO, PWR_MASK_L4, 0);
+            break;
+        default:
+            dprintf(CRITICAL, "[%s:%d]invalid value: 0x%x\n", __FILE__, __func__, bits);
+            break;
+    }
+}
+
+static void msdc_set_gpio(u32 bits)
+{
+    u32 l_val = 0;
+
+    switch (bits) {
+        case MSDC0_PWR_MASK_EN:
+            /* check for set before */
+            MSDC_SET_FIELD(PWR_GPIO,    MSDC0_PWR_MASK_EN,1);
+            MSDC_SET_FIELD(PWR_GPIO_EO, MSDC0_PWR_MASK_EN,1);
+            break;
+        case MSDC0_PWR_MASK_VOL_18:
+            /* check for set before */
+            MSDC_SET_FIELD(PWR_GPIO,    (MSDC0_PWR_MASK_VOL_18|MSDC0_PWR_MASK_VOL_33), 1);
+            MSDC_SET_FIELD(PWR_GPIO_EO, (MSDC0_PWR_MASK_VOL_18|MSDC0_PWR_MASK_VOL_33), 1);
+            break;
+        case MSDC0_PWR_MASK_VOL_33:
+            /* check for set before */
+            MSDC_SET_FIELD(PWR_GPIO,    (MSDC0_PWR_MASK_VOL_18|MSDC0_PWR_MASK_VOL_33), 2);
+            MSDC_SET_FIELD(PWR_GPIO_EO, (MSDC0_PWR_MASK_VOL_18|MSDC0_PWR_MASK_VOL_33), 2);
+            break;
+        case MSDC0_PWR_MASK_L4:
+            /* check for set before */
+            MSDC_SET_FIELD(PWR_GPIO,    MSDC0_PWR_MASK_L4, 1);
+            MSDC_SET_FIELD(PWR_GPIO_EO, MSDC0_PWR_MASK_L4, 1);
+            break;
+        case PWR_MASK_EN:
+            /* check for set before */
+            MSDC_SET_FIELD(PWR_GPIO,    PWR_MASK_EN,1);
+            MSDC_SET_FIELD(PWR_GPIO_EO, PWR_MASK_EN,1);
+            break;
+        case PWR_MASK_VOL_18:
+            /* check for set before */
+            MSDC_SET_FIELD(PWR_GPIO,    (PWR_MASK_VOL_18|PWR_MASK_VOL_33), 1);
+            MSDC_SET_FIELD(PWR_GPIO_EO, (PWR_MASK_VOL_18|PWR_MASK_VOL_33), 1);
+            break;
+        case PWR_MASK_VOL_33:
+            /* check for set before */
+            MSDC_SET_FIELD(PWR_GPIO,    (PWR_MASK_VOL_18|PWR_MASK_VOL_33), 2);
+            MSDC_SET_FIELD(PWR_GPIO_EO, (PWR_MASK_VOL_18|PWR_MASK_VOL_33), 2);
+            break;
+        case PWR_MASK_L4:
+            /* check for set before */
+            MSDC_SET_FIELD(PWR_GPIO,    PWR_MASK_L4, 1);
+            MSDC_SET_FIELD(PWR_GPIO_EO, PWR_MASK_L4, 1);
+            break;
+        default:
+            dprintf(CRITICAL, "[%s:%s]invalid value: 0x%x\n", __FILE__, __func__, bits);
+            break;
+    }
+}
+
+static void msdc_set_card_pwr(struct mmc_host *host, int on)
+{
+    if (on) {
+        msdc_set_gpio(MSDC0_PWR_MASK_VOL_18);
+        msdc_set_gpio(MSDC0_PWR_MASK_L4);
+        msdc_set_gpio(MSDC0_PWR_MASK_EN);
+    } else {
+        msdc_clr_gpio(MSDC0_PWR_MASK_EN);
+        msdc_clr_gpio(MSDC0_PWR_MASK_VOL_18);
+        msdc_clr_gpio(MSDC0_PWR_MASK_L4);
+    }
+    spin(10000);
+}
+
+static void msdc_set_host_level_pwr(struct mmc_host *host, u32 level)
+{
+    msdc_clr_gpio(PWR_MASK_VOL_18);
+    msdc_clr_gpio(PWR_MASK_VOL_33);
+
+    if (level) {
+        msdc_set_gpio(PWR_MASK_VOL_18);
+    } else {
+        msdc_set_gpio(PWR_MASK_VOL_33);
+    }
+    msdc_set_gpio(PWR_MASK_L4);
+}
+
+static void msdc_set_host_pwr(struct mmc_host *host, int on)
+{
+    msdc_set_host_level_pwr(host, 0);
+}
+#endif
+
+static void msdc_set_startbit(struct mmc_host *host, u8 start_bit)
+{
+    addr_t base = host->base;
+    msdc_priv_t *priv = (msdc_priv_t *)host->priv;
+
+    /* set start bit */
+    MSDC_SET_FIELD(MSDC_CFG, MSDC_CFG_START_BIT, start_bit);
+    priv->start_bit = start_bit;
+    LTRACEF("start bit = %d, MSDC_CFG[0x%x]\n", start_bit, MSDC_READ32(MSDC_CFG));
+}
+
+#define TYPE_CMD_RESP_EDGE      (0)
+#define TYPE_WRITE_CRC_EDGE     (1)
+#define TYPE_READ_DATA_EDGE     (2)
+#define TYPE_WRITE_DATA_EDGE    (3)
+
+static void msdc_set_smpl(struct mmc_host *host, u8 HS400, u8 mode, u8 type)
+{
+    addr_t base = host->base;
+    int i=0;
+    msdc_priv_t *priv = (msdc_priv_t *)host->priv;
+    static u8 read_data_edge[8] = {MSDC_SMPL_RISING, MSDC_SMPL_RISING, MSDC_SMPL_RISING, MSDC_SMPL_RISING,
+                                   MSDC_SMPL_RISING, MSDC_SMPL_RISING, MSDC_SMPL_RISING, MSDC_SMPL_RISING
+                                  };
+    static u8 write_data_edge[4] = {MSDC_SMPL_RISING, MSDC_SMPL_RISING, MSDC_SMPL_RISING, MSDC_SMPL_RISING};
+
+    switch (type) {
+        case TYPE_CMD_RESP_EDGE:
+            if (HS400) {
+                // eMMC5.0 only output resp at CLK pin, so no need to select DS pin
+                MSDC_SET_FIELD(EMMC50_CFG0, MSDC_EMMC50_CFG_PADCMD_LATCHCK, 0); //latch cmd resp at CLK pin
+                MSDC_SET_FIELD(EMMC50_CFG0, MSDC_EMMC50_CFG_CMD_RESP_SEL, 0);//latch cmd resp at CLK pin
+            }
+
+            if (mode == MSDC_SMPL_RISING || mode == MSDC_SMPL_FALLING) {
+                MSDC_SET_FIELD(MSDC_IOCON, MSDC_IOCON_RSPL, mode);
+                priv->rsmpl = mode;
+            } else {
+                dprintf(CRITICAL, "[%s]: invalid resp parameter: HS400=%d, type=%d, mode=%d\n", __func__, HS400, type, mode);
+            }
+            break;
+
+        case TYPE_WRITE_CRC_EDGE:
+            if (HS400) {
+                MSDC_SET_FIELD(EMMC50_CFG0, MSDC_EMMC50_CFG_CRC_STS_SEL, 1);//latch write crc status at DS pin
+            } else {
+                MSDC_SET_FIELD(EMMC50_CFG0, MSDC_EMMC50_CFG_CRC_STS_SEL, 0);//latch write crc status at CLK pin
+            }
+
+            if (mode == MSDC_SMPL_RISING || mode == MSDC_SMPL_FALLING) {
+                if (HS400) {
+                    MSDC_SET_FIELD(EMMC50_CFG0, MSDC_EMMC50_CFG_CRC_STS_EDGE, mode);
+                } else {
+                    MSDC_SET_FIELD(MSDC_IOCON, MSDC_IOCON_W_D_SMPL_SEL, 0);
+                    MSDC_SET_FIELD(MSDC_IOCON, MSDC_IOCON_W_D_SMPL, mode);
+                }
+                priv->wdsmpl = mode;
+            } else if (mode == MSDC_SMPL_SEPERATE && !HS400) {
+                MSDC_SET_FIELD(MSDC_IOCON, MSDC_IOCON_W_D0SPL, write_data_edge[0]); //only dat0 is for write crc status.
+                priv->wdsmpl = mode;
+            } else {
+                dprintf(CRITICAL, "[%s]: invalid crc parameter: HS400=%d, type=%d, mode=%d\n", __func__, HS400, type, mode);
+            }
+            break;
+
+        case TYPE_READ_DATA_EDGE:
+            if (HS400) {
+                msdc_set_startbit(host, START_AT_RISING_AND_FALLING); //for HS400, start bit is output both on rising and falling edge
+                priv->start_bit = START_AT_RISING_AND_FALLING;
+            } else {
+                msdc_set_startbit(host, START_AT_RISING); //for the other mode, start bit is only output on rising edge. but DDR50 can try falling edge if error casued by pad delay
+                priv->start_bit = START_AT_RISING;
+            }
+            if (mode == MSDC_SMPL_RISING || mode == MSDC_SMPL_FALLING) {
+                MSDC_SET_FIELD(MSDC_IOCON, MSDC_IOCON_R_D_SMPL_SEL, 0);
+                MSDC_SET_FIELD(MSDC_IOCON, MSDC_IOCON_R_D_SMPL, mode);
+                priv->rdsmpl = mode;
+            } else if (mode == MSDC_SMPL_SEPERATE) {
+                MSDC_SET_FIELD(MSDC_IOCON, MSDC_IOCON_R_D_SMPL_SEL, 1);
+                for (i=0; i<8; i++) {
+                    MSDC_SET_FIELD(MSDC_IOCON, (MSDC_IOCON_R_D0SPL << i), read_data_edge[i]);
+                }
+                priv->rdsmpl = mode;
+            } else {
+                dprintf(CRITICAL, "[%s]: invalid read parameter: HS400=%d, type=%d, mode=%d\n", __func__, HS400, type, mode);
+            }
+            break;
+
+        case TYPE_WRITE_DATA_EDGE:
+            MSDC_SET_FIELD(EMMC50_CFG0, MSDC_EMMC50_CFG_CRC_STS_SEL, 0);//latch write crc status at CLK pin
+
+            if (mode == MSDC_SMPL_RISING|| mode == MSDC_SMPL_FALLING) {
+                MSDC_SET_FIELD(MSDC_IOCON, MSDC_IOCON_W_D_SMPL_SEL, 0);
+                MSDC_SET_FIELD(MSDC_IOCON, MSDC_IOCON_W_D_SMPL, mode);
+                priv->wdsmpl = mode;
+            } else if (mode == MSDC_SMPL_SEPERATE) {
+                MSDC_SET_FIELD(MSDC_IOCON, MSDC_IOCON_W_D_SMPL_SEL, 1);
+                for (i=0; i<4; i++) {
+                    MSDC_SET_FIELD(MSDC_IOCON, (MSDC_IOCON_W_D0SPL << i), write_data_edge[i]);//dat0~4 is for SDIO card.
+                }
+                priv->wdsmpl = mode;
+            } else {
+                dprintf(CRITICAL, "[%s]: invalid write parameter: HS400=%d, type=%d, mode=%d\n", __func__, HS400, type, mode);
+            }
+            break;
+
+        default:
+            dprintf(CRITICAL, "[%s]: invalid parameter: HS400=%d, type=%d, mode=%d\n", __func__, HS400, type, mode);
+            break;
+    }
+}
+
+void msdc_set_timeout(struct mmc_host *host, u32 ns, u32 clks)
+{
+    addr_t base = host->base;
+    u32 timeout, clk_ns;
+    u32 mode = 0;
+
+    if (host->sclk == 0) {
+        timeout = 0;
+    } else {
+        clk_ns  = 1000000000UL / host->sclk;
+        timeout = (ns + clk_ns - 1) / clk_ns + clks;
+        timeout = (timeout + (1 << 20) - 1) >> 20; /* in 1048576 sclk cycle unit */
+        MSDC_GET_FIELD(MSDC_CFG, MSDC_CFG_CKMOD, mode);
+        timeout = mode >= 2 ? timeout * 2 : timeout; //DDR mode will double the clk cycles for data timeout
+        timeout = timeout > 1 ? timeout - 1 : 0;
+        timeout = timeout > 255 ? 255 : timeout;
+    }
+    MSDC_SET_FIELD(SDC_CFG, SDC_CFG_DTOC, timeout);
+    LTRACEF("[MSDC] Set read data timeout: %dns %dclks -> %d (65536 sclk cycles)\n",
+            ns, clks, timeout + 1);
+}
+
+void msdc_set_autocmd(struct mmc_host *host, int cmd)
+{
+    msdc_priv_t *priv = (msdc_priv_t *)host->priv;
+
+    priv->autocmd = cmd;
+}
+
+int msdc_get_autocmd(struct mmc_host *host)
+{
+    msdc_priv_t *priv = (msdc_priv_t *)host->priv;
+
+    return priv->autocmd;
+}
+
+static void msdc_abort(struct mmc_host *host)
+{
+    addr_t base = host->base;
+
+    dprintf(CRITICAL, "[MSDC] Abort: MSDC_FIFOCS=%xh MSDC_PS=%xh SDC_STS=%xh\n",
+            MSDC_READ32(MSDC_FIFOCS), MSDC_READ32(MSDC_PS), MSDC_READ32(SDC_STS));
+    /* reset controller */
+    MSDC_RESET();
+
+    /* clear fifo */
+    MSDC_CLR_FIFO();
+
+    /* make sure txfifo and rxfifo are empty */
+    if (MSDC_TXFIFOCNT() != 0 || MSDC_RXFIFOCNT() != 0) {
+        LTRACEF("[MSDC] Abort: TXFIFO(%d), RXFIFO(%d) != 0\n",
+                MSDC_TXFIFOCNT(), MSDC_RXFIFOCNT());
+    }
+
+    /* clear all interrupts */
+    MSDC_CLR_INT();
+}
+
+static int msdc_get_card_status(struct mmc_host *host, u32 *status)
+{
+    int err;
+    struct mmc_command cmd;
+
+    cmd.opcode  = MMC_CMD_SEND_STATUS;
+    cmd.arg     = host->card->rca << 16;
+    cmd.rsptyp  = RESP_R1;
+    cmd.retries = CMD_RETRIES;
+    cmd.timeout = CMD_TIMEOUT;
+
+    err = msdc_send_cmd(host, &cmd);
+    if (!err)
+        err = msdc_wait_cmd_done(host, &cmd);
+
+    if (err == MMC_ERR_NONE)
+        *status = cmd.resp[0];
+
+    return err;
+}
+
+int msdc_abort_handler(struct mmc_host *host, int abort_card)
+{
+    struct mmc_command stop;
+    u32 status = 0;
+    u32 state = 0;
+
+    while (state != 4) { // until status to "tran"
+        msdc_abort(host);
+        if (msdc_get_card_status(host, &status)) {
+            dprintf(CRITICAL, "Get card status failed\n");
+            return 1;
+        }
+        state = R1_CURRENT_STATE(status);
+        LTRACEF("check card state<%d>\n", state);
+        if (state == 5 || state == 6) {
+            LTRACEF("state<%d> need cmd12 to stop\n", state);
+            if (abort_card) {
+                stop.opcode  = MMC_CMD_STOP_TRANSMISSION;
+                stop.rsptyp  = RESP_R1B;
+                stop.arg     = 0;
+                stop.retries = CMD_RETRIES;
+                stop.timeout = CMD_TIMEOUT;
+                msdc_send_cmd(host, &stop);
+                msdc_wait_cmd_done(host, &stop); // don't tuning
+            } else if (state == 7) {  // busy in programing
+                LTRACEF("state<%d> card is busy\n", state);
+                spin(100000);
+            } else if (state != 4) {
+                LTRACEF("state<%d> ??? \n", state);
+                return 1;
+            }
+        }
+    }
+    msdc_abort(host);
+    return 0;
+}
+
+static u32 msdc_intr_wait(struct mmc_host *host, u32 intrs)
+{
+    u32 sts = 0;
+    u32 tmo = UINT_MAX;
+    int ret = 0;
+
+    /* warning that interrupts are not enabled */
+    ret = event_wait_timeout(&msdc_int_event, tmo);
+    if (ret != 0) {
+        addr_t base = host->base;
+        dprintf(CRITICAL, "[%s]: failed to get event INT=0x%x\n",
+                __func__, MSDC_READ32(MSDC_INT));
+        g_int_status = 0;
+        return 0;
+    }
+
+    sts = g_int_status;
+    g_int_status = 0;
+
+    if (~intrs & sts)
+        dprintf(CRITICAL, "[MSDC]<CHECKME> Unexpected INT(0x%x)\n", ~intrs & sts);
+
+    return sts;
+}
+
+static enum handler_return msdc_interrupt_handler(void *arg)
+{
+    struct mmc_host *host = arg;
+    addr_t base = host->base;
+
+    /* Save & Clear the interrupt */
+    g_int_status = MSDC_READ32(MSDC_INT);
+    MSDC_WRITE32(MSDC_INT, g_int_status & host->intr_mask);
+    MSDC_WRITE32(MSDC_INTEN, 0);
+    host->intr_mask = 0;
+
+    /* MUST BE *false*! otherwise, schedule in interrupt */
+    event_signal(&msdc_int_event, false);
+
+    return INT_RESCHEDULE;
+}
+
+static int msdc_send_cmd(struct mmc_host *host, struct mmc_command *cmd)
+{
+    msdc_priv_t *priv = (msdc_priv_t *)host->priv;
+    addr_t base   = host->base;
+    u32 opcode = cmd->opcode;
+    u32 rsptyp = cmd->rsptyp;
+    u32 rawcmd;
+    u32 error = MMC_ERR_NONE;
+
+    /* rawcmd :
+     * vol_swt << 30 | auto_cmd << 28 | blklen << 16 | go_irq << 15 |
+     * stop << 14 | rw << 13 | dtype << 11 | rsptyp << 7 | brk << 6 | opcode
+     */
+    rawcmd = (opcode & ~(SD_CMD_BIT | SD_CMD_APP_BIT)) |
+             msdc_rsp[rsptyp] << 7 | host->blklen << 16;
+
+    if (opcode == MMC_CMD_WRITE_MULTIPLE_BLOCK) {
+        rawcmd |= ((2 << 11) | (1 << 13));
+        if (priv->autocmd & MSDC_AUTOCMD12) {
+            rawcmd |= (1 << 28);
+        } else if (priv->autocmd & MSDC_AUTOCMD23) {
+            rawcmd |= (2 << 28);
+        }
+    } else if (opcode == MMC_CMD_WRITE_BLOCK || opcode == MMC_CMD50) {
+        rawcmd |= ((1 << 11) | (1 << 13));
+    } else if (opcode == MMC_CMD_READ_MULTIPLE_BLOCK) {
+        rawcmd |= (2 << 11);
+        if (priv->autocmd & MSDC_AUTOCMD12) {
+            rawcmd |= (1 << 28);
+        } else if (priv->autocmd & MSDC_AUTOCMD23) {
+            rawcmd |= (2 << 28);
+        }
+    } else if (opcode == MMC_CMD_READ_SINGLE_BLOCK ||
+               opcode == SD_ACMD_SEND_SCR ||
+               opcode == SD_CMD_SWITCH ||
+               opcode == MMC_CMD_SEND_EXT_CSD ||
+               opcode == MMC_CMD_SEND_WRITE_PROT ||
+               opcode == MMC_CMD_SEND_WRITE_PROT_TYPE ||
+               opcode == MMC_CMD21) {
+        rawcmd |= (1 << 11);
+    } else if (opcode == MMC_CMD_STOP_TRANSMISSION) {
+        rawcmd |= (1 << 14);
+        rawcmd &= ~(0x0FFF << 16);
+    } else if (opcode == SD_IO_RW_EXTENDED) {
+        if (cmd->arg & 0x80000000)  /* R/W flag */
+            rawcmd |= (1 << 13);
+        if ((cmd->arg & 0x08000000) && ((cmd->arg & 0x1FF) > 1))
+            rawcmd |= (2 << 11); /* multiple block mode */
+        else
+            rawcmd |= (1 << 11);
+    } else if (opcode == SD_IO_RW_DIRECT) {
+        if ((cmd->arg & 0x80000000) && ((cmd->arg >> 9) & 0x1FFFF))/* I/O abt */
+            rawcmd |= (1 << 14);
+    } else if (opcode == SD_CMD_VOL_SWITCH) {
+        rawcmd |= (1 << 30);
+    } else if (opcode == SD_CMD_SEND_TUNING_BLOCK) {
+        rawcmd |= (1 << 11); /* CHECKME */
+        if (priv->autocmd & MSDC_AUTOCMD19)
+            rawcmd |= (3 << 28);
+    } else if (opcode == MMC_CMD_GO_IRQ_STATE) {
+        rawcmd |= (1 << 15);
+    } else if (opcode == MMC_CMD_WRITE_DAT_UNTIL_STOP) {
+        rawcmd |= ((1<< 13) | (3 << 11));
+    } else if (opcode == MMC_CMD_READ_DAT_UNTIL_STOP) {
+        rawcmd |= (3 << 11);
+    }
+
+    LTRACEF("+[MSDC%d] CMD(%d): ARG(0x%x), RAW(0x%x), BLK_NUM(0x%x) RSP(%d)\n",host->host_id,
+            (opcode & ~(SD_CMD_BIT | SD_CMD_APP_BIT)), cmd->arg, rawcmd,
+            MSDC_READ32(SDC_BLK_NUM), rsptyp);
+
+    while (SDC_IS_CMD_BUSY());
+    if ((rsptyp == RESP_R1B) || (opcode == MMC_CMD_WRITE_MULTIPLE_BLOCK) ||
+            opcode == MMC_CMD_WRITE_BLOCK || opcode == MMC_CMD_READ_MULTIPLE_BLOCK ||
+            opcode == MMC_CMD_READ_SINGLE_BLOCK)
+        while (SDC_IS_BUSY());
+
+    SDC_SEND_CMD(rawcmd, cmd->arg);
+
+end:
+    cmd->error = error;
+
+    return error;
+}
+
+static int msdc_wait_cmd_done(struct mmc_host *host, struct mmc_command *cmd)
+{
+    addr_t base   = host->base;
+    u32 rsptyp = cmd->rsptyp;
+    u32 status;
+    u32 opcode = (cmd->opcode & ~(SD_CMD_BIT | SD_CMD_APP_BIT));
+    u32 error = MMC_ERR_NONE;
+    u32 wints = MSDC_INT_CMDTMO | MSDC_INT_CMDRDY | MSDC_INT_RSPCRCERR |
+                MSDC_INT_ACMDRDY | MSDC_INT_ACMDCRCERR | MSDC_INT_ACMDTMO;
+    u32 *resp = &cmd->resp[0];
+    msdc_priv_t *priv = (msdc_priv_t *)host->priv;
+
+    while (1) {
+        /* Wait for interrupt coming */
+        while (((status = MSDC_READ32(MSDC_INT)) & wints) == 0);
+        MSDC_WRITE32(MSDC_INT, (status & wints));
+        if (~wints & status)
+            dprintf(CRITICAL, "[MSDC]<CHECKME> Unexpected INT(0x%x)\n",
+                    ~wints & status);
+
+        if (status & MSDC_INT_CMDRDY)
+            break;
+        else if (status & MSDC_INT_RSPCRCERR) {
+            if (opcode != MMC_CMD21)
+                dprintf(CRITICAL, "[MSDC] cmd%d CRCERR! (0x%x)\n", opcode, status);
+            error = MMC_ERR_BADCRC;
+            goto err;
+        } else if (status & MSDC_INT_CMDTMO) {
+            dprintf(CRITICAL, "[MSDC] cmd%d TMO! (0x%x)\n", opcode, status);
+            error = MMC_ERR_TIMEOUT;
+            goto err;
+        } else if (priv->autocmd & MSDC_AUTOCMD23) {
+            if (status & MSDC_INT_ACMDRDY)
+                /* Autocmd rdy is set prior to cmd rdy */
+                continue;
+            else if (status & MSDC_INT_ACMDCRCERR) {
+                dprintf(CRITICAL, "[MSDC] autocmd23 CRCERR! (0x%x)\n", status);
+                error = MMC_ERR_ACMD_RSPCRC;
+                goto err;
+            } else if (status & MSDC_INT_ACMDTMO) {
+                dprintf(CRITICAL, "[MSDC] autocmd23 TMO! (0x%x)\n", status);
+                error = MMC_ERR_ACMD_TIMEOUT;
+                goto err;
+            }
+        } else {
+            dprintf(CRITICAL, "[MSDC] cmd%d UNEXPECT status! (0x%x)\n",
+                    opcode, status);
+            error = MMC_ERR_UNEXPECT;
+            goto err;
+        }
+    }
+
+    switch (rsptyp) {
+        case RESP_NONE:
+            LTRACEF("-[MSDC] CMD(%d): RSP(%d)\n",
+                    opcode, rsptyp);
+            break;
+        case RESP_R2:
+            *resp++ = MSDC_READ32(SDC_RESP3);
+            *resp++ = MSDC_READ32(SDC_RESP2);
+            *resp++ = MSDC_READ32(SDC_RESP1);
+            *resp++ = MSDC_READ32(SDC_RESP0);
+            LTRACEF("-[MSDC] CMD(%d): RSP(%d) = 0x%x 0x%x 0x%x 0x%x\n",
+                    opcode, cmd->rsptyp, cmd->resp[0], cmd->resp[1],
+                    cmd->resp[2], cmd->resp[3]);
+            break;
+        default: /* Response types 1, 3, 4, 5, 6, 7(1b) */
+            cmd->resp[0] = MSDC_READ32(SDC_RESP0);
+            LTRACEF("-[MSDC] CMD(%d): RSP(%d) = 0x%x\n",
+                    opcode, cmd->rsptyp, cmd->resp[0]);
+            break;
+    }
+
+err:
+    if (rsptyp == RESP_R1B)
+        while ((MSDC_READ32(MSDC_PS) & MSDC_PS_DAT0) != MSDC_PS_DAT0);
+
+    cmd->error = error;
+
+    return error;
+}
+
+int msdc_cmd(struct mmc_host *host, struct mmc_command *cmd)
+{
+    int err;
+
+    err = msdc_send_cmd(host, cmd);
+    if (err != MMC_ERR_NONE)
+        return err;
+
+    err = msdc_wait_cmd_done(host, cmd);
+
+    /*
+     * For CMD21 resp CRC error, sitll need receive data, so MUST not
+     * clear fifo or do host reset
+     */
+    if (err && cmd->opcode != MMC_CMD21) {
+        addr_t base = host->base;
+        u32 tmp = MSDC_READ32(SDC_CMD);
+
+        /* check if data is used by the command or not */
+        if (tmp & SDC_CMD_DTYP) {
+            if (msdc_abort_handler(host, 1)) {
+                dprintf(CRITICAL, "[MSDC] abort failed\n");
+            }
+        }
+
+        if (cmd->opcode == MMC_CMD_APP_CMD ||
+                cmd->opcode == SD_CMD_SEND_IF_COND) {
+            if (err ==  MMC_ERR_TIMEOUT)
+                return err;
+        }
+
+        err = msdc_tune_cmdrsp(host, cmd);
+    }
+
+    return err;
+}
+
+#ifdef MSDC_USE_DMA_MODE
+static void msdc_flush_membuf(void *buf, u32 len)
+{
+    arch_clean_invalidate_cache_range((addr_t)buf,len);
+}
+
+static int msdc_dma_wait_done(struct mmc_host *host, struct mmc_command *cmd)
+{
+    addr_t base = host->base;
+    msdc_priv_t *priv = (msdc_priv_t *)host->priv;
+    u32 status;
+    u32 error = MMC_ERR_NONE;
+    u32 wints = MSDC_INT_XFER_COMPL | MSDC_INT_DATTMO | MSDC_INT_DATCRCERR |
+                MSDC_INT_DXFER_DONE | MSDC_INT_DMAQ_EMPTY |
+                MSDC_INT_ACMDRDY | MSDC_INT_ACMDTMO | MSDC_INT_ACMDCRCERR |
+                MSDC_INT_CMDRDY | MSDC_INT_CMDTMO | MSDC_INT_RSPCRCERR;
+
+    /* Deliver it to irq handler */
+    host->intr_mask = wints;
+
+    do {
+        status = msdc_intr_wait(host, wints);
+
+        if (status & MSDC_INT_XFER_COMPL) {
+            if (mmc_op_multi(cmd->opcode) && (priv->autocmd & MSDC_AUTOCMD12)) {
+                /* acmd rdy should be checked after xfer_done been held */
+                if (status & MSDC_INT_ACMDRDY) {
+                    break;
+                } else if (status & MSDC_INT_ACMDTMO) {
+                    dprintf(CRITICAL, "[MSDC] ACMD12 timeout(%xh)\n", status);
+                    error = MMC_ERR_ACMD_TIMEOUT;
+                    goto end;
+                } else if (status & MSDC_INT_ACMDCRCERR) {
+                    dprintf(CRITICAL, "[MSDC] ACMD12 CRC error(%xh)\n", status);
+                    error = MMC_ERR_ACMD_RSPCRC;
+                    goto end;
+                }
+            } else
+                break;
+        }
+
+        if (status == 0 || status & MSDC_INT_DATTMO) {
+            dprintf(CRITICAL, "[MSDC] DMA DAT timeout(%xh)\n", status);
+            error = MMC_ERR_TIMEOUT;
+            goto end;
+        } else if (status & MSDC_INT_DATCRCERR) {
+		if (cmd->opcode != MMC_CMD21)
+			dprintf(CRITICAL, "[MSDC] DMA DAT CRC error(%xh)\n", status);
+            error = MMC_ERR_BADCRC;
+            goto end;
+        } else {
+            dprintf(CRITICAL, "[MSDC] Unexpect status(0x%x)\n", status);
+            error = MMC_ERR_UNEXPECT;
+            goto end;
+        }
+    } while (1);
+
+end:
+    if (error)
+        MSDC_RESET();
+
+    return error;
+}
+
+int msdc_dma_transfer(struct mmc_host *host, struct mmc_data *data)
+{
+    addr_t base = host->base;
+    int err;
+    paddr_t pa;
+
+    /* Set dma timeout */
+    msdc_set_timeout(host, data->timeout * 1000000, 0);
+    /* DRAM address */
+#if WITH_KERNEL_VM
+    pa = kvaddr_to_paddr(data->buf);
+#else
+    pa = (paddr_t)(data->buf);
+#endif
+    if (sizeof(pa) > 4)
+        LTRACEF("[MSDC] WARN: 64bit physical address!\n");
+    MSDC_WRITE32(MSDC_DMA_SA, (u32)pa);
+    MSDC_SET_FIELD(MSDC_DMA_CTRL, MSDC_DMA_CTRL_BURSTSZ, MSDC_DMA_BURST_64B);
+    /* BASIC_DMA mode */
+    MSDC_SET_FIELD(MSDC_DMA_CTRL, MSDC_DMA_CTRL_MODE, 0);
+    /* This is the last buffer */
+    MSDC_SET_FIELD(MSDC_DMA_CTRL, MSDC_DMA_CTRL_LASTBUF, 1);
+    /* Total transfer size */
+    MSDC_WRITE32(MSDC_DMA_LEN, data->blks * host->blklen);
+    /* Set interrupts bit */
+    MSDC_SET_BIT32(MSDC_INTEN,
+                   MSDC_INT_XFER_COMPL | MSDC_INT_DATTMO | MSDC_INT_DATCRCERR);
+    /* Clean & Invalidate cache */
+    msdc_flush_membuf(data->buf, data->blks * host->blklen);
+    /* Trigger DMA start */
+    MSDC_SET_FIELD(MSDC_DMA_CTRL, MSDC_DMA_CTRL_START, 1);
+    /* wait DMA transferring done */
+    err = msdc_dma_wait_done(host, data->cmd);
+    msdc_flush_membuf(data->buf, data->blks * host->blklen);
+    if (err) {
+	    /*
+	     * We do not want print out error logs of CMD21, As it may
+	     * let user confused.
+	     */
+	    if (data->cmd->opcode == MMC_CMD21) {
+		    /* reset controller */
+		    MSDC_RESET();
+
+		    /* clear fifo */
+		    MSDC_CLR_FIFO();
+
+		    /* make sure txfifo and rxfifo are empty */
+		    if (MSDC_TXFIFOCNT() != 0 || MSDC_RXFIFOCNT() != 0) {
+			    LTRACEF("[MSDC] Abort: TXFIFO(%d), RXFIFO(%d) != 0\n",
+					    MSDC_TXFIFOCNT(), MSDC_RXFIFOCNT());
+		    }
+
+		    /* clear all interrupts */
+		    MSDC_CLR_INT();
+
+	    } else {
+		    dprintf(CRITICAL, "[MSDC] DMA failed! err(%d)\n", err);
+		    if (msdc_abort_handler(host, 1)) {
+			    dprintf(CRITICAL, "[MSDC] eMMC cannot back to TRANS mode!\n");
+			    return MMC_ERR_FAILED;
+		    }
+	    }
+    }
+
+    /* Check DMA status and stop DMA transfer */
+    MSDC_SET_FIELD(MSDC_DMA_CTRL, MSDC_DMA_CTRL_STOP, 1);
+    while (MSDC_READ32(MSDC_DMA_CFG) & MSDC_DMA_CFG_STS);
+
+    return err;
+}
+
+static int msdc_dma_rw(struct mmc_host *host, u8 *buf, u32 blkaddr, u32 nblks, bool rd)
+{
+    int multi, err;
+    struct mmc_command cmd;
+    struct mmc_data data;
+    addr_t base = host->base;
+
+    ASSERT(nblks <= host->max_phys_segs);
+
+    LTRACEF("[MSDC] %s data %d blks %s 0x%x\n",
+            rd ? "Read" : "Write", nblks, rd ? "from" : "to", blkaddr);
+
+    multi = nblks > 1 ? 1 : 0;
+    /* DMA and block number _MUST_BE_ set prior to issuing command */
+    MSDC_DMA_ON;
+    MSDC_WRITE32(SDC_BLK_NUM, nblks);
+
+    /* send read command */
+    if (rd)
+        cmd.opcode =
+            multi ? MMC_CMD_READ_MULTIPLE_BLOCK : MMC_CMD_READ_SINGLE_BLOCK;
+    else
+        cmd.opcode = multi ? MMC_CMD_WRITE_MULTIPLE_BLOCK : MMC_CMD_WRITE_BLOCK;
+    cmd.arg = blkaddr;
+    cmd.rsptyp  = RESP_R1;
+    cmd.retries = 0;
+    cmd.timeout = CMD_TIMEOUT;
+
+    err = msdc_cmd(host, &cmd);
+    if (err != MMC_ERR_NONE)
+        return err;
+
+    data.cmd = &cmd;
+    data.blks = nblks;
+    data.buf = buf;
+    if (rd)
+        data.timeout = 100;
+    else
+        data.timeout = 250;
+
+    err = msdc_dma_transfer(host, &data);
+    MSDC_DMA_OFF;
+
+    return err;
+}
+
+static int msdc_dma_bread(struct mmc_host *host, u8 *dst, u32 src, u32 nblks)
+{
+    return msdc_dma_rw(host, dst, src, nblks, true);
+}
+
+static int msdc_dma_bwrite(struct mmc_host *host, u32 dst, u8 *src, u32 nblks)
+{
+    return msdc_dma_rw(host, src, dst, nblks, false);
+}
+#else
+static int msdc_pio_read_word(struct mmc_host *host, u32 *ptr, u32 size)
+{
+    int err = MMC_ERR_NONE;
+    addr_t base = host->base;
+    u32 ints = MSDC_INT_DATCRCERR | MSDC_INT_DATTMO | MSDC_INT_XFER_COMPL;
+    //u32 timeout = 100000;
+    u32 status;
+    u32 totalsz = size;
+    u8  done = 0;
+    u8 *u8ptr;
+    u32 dcrc=0;
+
+    while (1) {
+        status = MSDC_READ32(MSDC_INT);
+        MSDC_WRITE32(MSDC_INT, status);
+        if (status & ~ints)
+            dprintf(CRITICAL, "[MSDC]<CHECKME> Unexpected INT(0x%x)\n", status);
+        if (status & MSDC_INT_DATCRCERR) {
+            MSDC_GET_FIELD(SDC_DCRC_STS, SDC_DCRC_STS_POS|SDC_DCRC_STS_NEG, dcrc);
+            dprintf(CRITICAL, "[MSDC] DAT CRC error (0x%x), Left:%d/%d bytes, RXFIFO:%d,dcrc:0x%x\n",
+                    status, size, totalsz, MSDC_RXFIFOCNT(),dcrc);
+            err = MMC_ERR_BADCRC;
+            break;
+        } else if (status & MSDC_INT_DATTMO) {
+            dprintf(CRITICAL, "[MSDC] DAT TMO error (0x%x), Left: %d/%d bytes, RXFIFO:%d\n",
+                    status, size, totalsz, MSDC_RXFIFOCNT());
+            err = MMC_ERR_TIMEOUT;
+            break;
+        } else if (status & MSDC_INT_ACMDCRCERR) {
+            MSDC_GET_FIELD(SDC_DCRC_STS, SDC_DCRC_STS_POS|SDC_DCRC_STS_NEG, dcrc);
+            dprintf(CRITICAL, "[MSDC] AUTOCMD CRC error (0x%x), Left:%d/%d bytes, RXFIFO:%d,dcrc:0x%x\n",
+                    status, size, totalsz, MSDC_RXFIFOCNT(),dcrc);
+            err = MMC_ERR_ACMD_RSPCRC;
+            break;
+        } else if (status & MSDC_INT_XFER_COMPL) {
+            done = 1;
+        }
+
+        if (size == 0 && done)
+            break;
+
+        /* Note. RXFIFO count would be aligned to 4-bytes alignment size */
+        if ((size >=  MSDC_FIFO_THD) && (MSDC_RXFIFOCNT() >= MSDC_FIFO_THD)) {
+            int left = MSDC_FIFO_THD >> 2;
+            do {
+                *ptr++ = MSDC_FIFO_READ32();
+            } while (--left);
+            size -= MSDC_FIFO_THD;
+            LTRACEF("[MSDC] Read %d bytes, RXFIFOCNT: %d,  Left: %d/%d\n",
+                    MSDC_FIFO_THD, MSDC_RXFIFOCNT(), size, totalsz);
+        } else if ((size < MSDC_FIFO_THD) && MSDC_RXFIFOCNT() >= size) {
+            while (size) {
+                if (size > 3) {
+                    *ptr++ = MSDC_FIFO_READ32();
+                } else {
+                    u8ptr = (u8 *)ptr;
+                    while (size --)
+                        *u8ptr++ = MSDC_FIFO_READ8();
+                }
+            }
+            LTRACEF("[MSDC] Read left bytes, RXFIFOCNT: %d, Left: %d/%d\n",
+                    MSDC_RXFIFOCNT(), size, totalsz);
+        }
+    }
+
+    return err;
+}
+
+static int msdc_pio_read(struct mmc_host *host, u32 *ptr, u32 size)
+{
+    int err = msdc_pio_read_word(host, (u32 *)ptr, size);
+
+    if (err != MMC_ERR_NONE) {
+        msdc_abort(host); /* reset internal fifo and state machine */
+        dprintf(CRITICAL, "[MSDC] %d-bit PIO Read Error (%d)\n", 32, err);
+    }
+
+    return err;
+}
+
+static int msdc_pio_write_word(struct mmc_host *host, u32 *ptr, u32 size)
+{
+    int err = MMC_ERR_NONE;
+    addr_t base = host->base;
+    u32 ints = MSDC_INT_DATCRCERR | MSDC_INT_DATTMO | MSDC_INT_XFER_COMPL;
+    //u32 timeout = 250000;
+    u32 status;
+    u8 *u8ptr;
+    msdc_priv_t *priv = (msdc_priv_t *)host->priv;
+
+    while (1) {
+        status = MSDC_READ32(MSDC_INT);
+        MSDC_WRITE32(MSDC_INT, status);
+        if (status & ~ints) {
+            dprintf(CRITICAL, "[MSDC]<CHECKME> Unexpected INT(0x%x)\n", status);
+        }
+        if (status & MSDC_INT_DATCRCERR) {
+            dprintf(CRITICAL, "[MSDC] DAT CRC error (0x%x), Left DAT: %d bytes\n",
+                    status, size);
+            err = MMC_ERR_BADCRC;
+            break;
+        } else if (status & MSDC_INT_DATTMO) {
+            dprintf(CRITICAL, "[MSDC] DAT TMO error (0x%x), Left DAT: %d bytes, MSDC_FIFOCS=%xh\n",
+                    status, size, MSDC_READ32(MSDC_FIFOCS));
+            err = MMC_ERR_TIMEOUT;
+            break;
+        } else if (status & MSDC_INT_ACMDCRCERR) {
+            dprintf(CRITICAL, "[MSDC] AUTO CMD CRC error (0x%x), Left DAT: %d bytes\n",
+                    status, size);
+            err = MMC_ERR_ACMD_RSPCRC;
+            break;
+        } else if (status & MSDC_INT_XFER_COMPL) {
+            if (size == 0) {
+                LTRACEF("[MSDC] all data flushed to card\n");
+                break;
+            } else
+                LTRACEF("[MSDC]<CHECKME> XFER_COMPL before all data written\n");
+        }
+
+        if (size == 0)
+            continue;
+
+        if (size >= MSDC_FIFO_SZ) {
+            if (MSDC_TXFIFOCNT() == 0) {
+                int left = MSDC_FIFO_SZ >> 2;
+                do {
+                    MSDC_FIFO_WRITE32(*ptr);
+                    ptr++;
+                } while (--left);
+                size -= MSDC_FIFO_SZ;
+            }
+        } else if (size < MSDC_FIFO_SZ && MSDC_TXFIFOCNT() == 0) {
+            while (size ) {
+                if (size > 3) {
+                    MSDC_FIFO_WRITE32(*ptr);
+                    ptr++;
+                    size -= 4;
+                } else {
+                    u8ptr = (u8 *)ptr;
+                    while (size --) {
+                        MSDC_FIFO_WRITE8(*u8ptr);
+                        u8ptr++;
+                    }
+                }
+            }
+        }
+    }
+
+    return err;
+}
+
+static int msdc_pio_write(struct mmc_host *host, u32 *ptr, u32 size)
+{
+    int err = msdc_pio_write_word(host, (u32 *)ptr, size);
+
+    if (err != MMC_ERR_NONE) {
+        msdc_abort(host); /* reset internal fifo and state machine */
+        dprintf(CRITICAL, "[MSDC] PIO Write Error (%d)\n", err);
+    }
+
+    return err;
+}
+
+static int msdc_pio_bread(struct mmc_host *host, u8 *dst, u32 src, u32 nblks)
+{
+    msdc_priv_t *priv = (msdc_priv_t *)host->priv;
+    addr_t base = host->base;
+    u32 blksz = host->blklen;
+    int err = MMC_ERR_NONE, derr = MMC_ERR_NONE;
+    int multi;
+    struct mmc_command cmd;
+    struct mmc_command stop;
+    u32 *ptr = (u32 *)dst;
+
+    LTRACEF("[MSDC] Read data %d bytes from 0x%x\n", nblks * blksz, src);
+
+    multi = nblks > 1 ? 1 : 0;
+
+    MSDC_CLR_FIFO();
+    MSDC_WRITE32(SDC_BLK_NUM, nblks);
+    msdc_set_timeout(host, 100000000, 0);
+
+    /* send read command */
+    cmd.opcode  = multi ? MMC_CMD_READ_MULTIPLE_BLOCK : MMC_CMD_READ_SINGLE_BLOCK;
+    cmd.rsptyp  = RESP_R1;
+    cmd.arg     = src;
+    cmd.retries = 0;
+    cmd.timeout = CMD_TIMEOUT;
+    err = msdc_cmd(host, &cmd);
+
+    if (err != MMC_ERR_NONE)
+        goto done;
+
+    err = derr = msdc_pio_read(host, (u32 *)ptr, nblks * blksz);
+
+done:
+    if (err != MMC_ERR_NONE) {
+        if (derr != MMC_ERR_NONE) {
+            dprintf(CRITICAL, "[MSDC] Read data error (%d)\n", derr);
+            if (msdc_abort_handler(host, 1))
+                dprintf(CRITICAL, "[MSDC] abort failed\n");
+        } else {
+            dprintf(CRITICAL, "[MSDC] Read error (%d)\n", err);
+        }
+    }
+    return (derr == MMC_ERR_NONE) ? err : derr;
+}
+
+static int msdc_pio_bwrite(struct mmc_host *host, u32 dst, u8 *src, u32 nblks)
+{
+    msdc_priv_t *priv = (msdc_priv_t *)host->priv;
+    addr_t base = host->base;
+    int err = MMC_ERR_NONE, derr = MMC_ERR_NONE;
+    int multi;
+    u32 blksz = host->blklen;
+    struct mmc_command cmd;
+    struct mmc_command stop;
+    u32 *ptr = (u32 *)src;
+
+    dprintf(CRITICAL, "[MSDC] Write data %d bytes to 0x%x\n", nblks * blksz, dst);
+
+    multi = nblks > 1 ? 1 : 0;
+
+    MSDC_CLR_FIFO();
+    MSDC_WRITE32(SDC_BLK_NUM, nblks);
+
+    /* No need since MSDC always waits 8 cycles for write data timeout */
+
+    /* send write command */
+    cmd.opcode  = multi ? MMC_CMD_WRITE_MULTIPLE_BLOCK : MMC_CMD_WRITE_BLOCK;
+    cmd.rsptyp  = RESP_R1;
+    cmd.arg     = dst;
+    cmd.retries = 0;
+    cmd.timeout = CMD_TIMEOUT;
+    err = msdc_cmd(host, &cmd);
+
+    if (err != MMC_ERR_NONE)
+        goto done;
+
+    err = derr = msdc_pio_write(host, (u32 *)ptr, nblks * blksz);
+
+done:
+    if (err != MMC_ERR_NONE) {
+        if (derr != MMC_ERR_NONE) {
+            dprintf(CRITICAL, "[MSDC] Write data error (%d)\n", derr);
+            if (msdc_abort_handler(host, 1))
+                dprintf(CRITICAL, "[MSDC] abort failed\n");
+        } else {
+            dprintf(CRITICAL, "[MSDC] Write error (%d)\n", err);
+        }
+    }
+    return (derr == MMC_ERR_NONE) ? err : derr;
+}
+#endif
+
+
+static void msdc_config_clksrc(struct mmc_host *host, u32 clksrc, u32 hclksrc)
+{
+    // modify the clock
+    ASSERT(host);
+    /*
+     * For MT2712, MSDC0 use 400Mhz(MSDCPLL) source clock
+     */
+    host->clksrc  = clksrc;
+    host->hclksrc = hclksrc;
+#ifndef FPGA_PLATFORM
+    if (host->host_id == 0)
+        host->clk     = 400 * 1000 * 1000;
+    else
+        host->clk     = 200 * 1000 * 1000;
+#else
+    host->clk = MSDC_OP_SCLK;
+#endif
+
+    /* Chaotian, may need update this part of code */
+    LTRACEF("[info][%s] pll_clk %u (%uMHz), pll_hclk %u\n",
+            __func__, host->clksrc, host->clk/1000000, host->hclksrc);
+}
+
+void msdc_config_clock(struct mmc_host *host, int state, u32 hz)
+{
+    addr_t base = host->base;
+    u32 mode = 0;
+    u32 div;
+    u32 sclk;
+    u32 u4buswidth=0;
+
+    if (hz >= host->f_max) {
+        hz = host->f_max;
+    } else if (hz < host->f_min) {
+        hz = host->f_min;
+    }
+
+    msdc_config_clksrc(host, host->clksrc, host->hclksrc);
+    MSDC_CLR_BIT32(MSDC_CFG, MSDC_CFG_CKMOD_HS400);
+    MSDC_SET_BIT32(MSDC_PATCH_BIT2, MSDC_PB2_CFGCRCSTS);
+
+    if (state & MMC_STATE_HS400) {
+        mode = 0x3;
+        div = 0; /* we let hs400 mode fixed at 200Mhz */
+        sclk = host->clk >> 1;
+        MSDC_SET_BIT32(MSDC_CFG, MSDC_CFG_CKMOD_HS400);
+        MSDC_CLR_BIT32(MSDC_PATCH_BIT2, MSDC_PB2_CFGCRCSTS);
+    } else if (state&MMC_STATE_DDR) {
+        mode = 0x2; /* ddr mode and use divisor */
+        if (hz >= (host->clk >> 2)) {
+            div  = 0;              /* mean div = 1/2 */
+            sclk = host->clk >> 2; /* sclk = clk/div/2. 2: internal divisor */
+        } else {
+            div  = (host->clk + ((hz << 2) - 1)) / (hz << 2);
+            sclk = (host->clk >> 2) / div;
+            div  = (div >> 1);     /* since there is 1/2 internal divisor */
+        }
+    } else if (hz >= host->clk) {
+        mode = 0x1; /* no divisor and divisor is ignored */
+        div  = 0;
+        sclk = host->clk;
+    } else {
+        mode = 0x0; /* use divisor */
+        if (hz >= (host->clk >> 1)) {
+            div  = 0;              /* mean div = 1/2 */
+            sclk = host->clk >> 1; /* sclk = clk / 2 */
+        } else {
+            div  = (host->clk + ((hz << 2) - 1)) / (hz << 2);
+            sclk = (host->clk >> 2) / div;
+        }
+    }
+    host->sclk = sclk;
+
+    MSDC_CLR_BIT32(PERI_MSDC_CLK_EN, (0x1 << 0));
+    /* set clock mode and divisor */
+    MSDC_SET_FIELD(MSDC_CFG, (MSDC_CFG_CKMOD |MSDC_CFG_CKDIV),\
+                   (mode << 12) | div);
+    MSDC_SET_BIT32(PERI_MSDC_CLK_EN, (0x1 << 0));
+    /* wait clock stable */
+    while (!(MSDC_READ32(MSDC_CFG) & MSDC_CFG_CKSTB));
+
+    MSDC_GET_FIELD(SDC_CFG,SDC_CFG_BUSWIDTH,u4buswidth);
+
+    LTRACEF(
+            "[MSDC] SET_CLK(%dkHz): SCLK(%dkHz) MODE(%d) DIV(%d) DS(%d) RS(%d) buswidth(%s)\n",
+            hz/1000, sclk/1000, mode, div, msdc_cap[host->host_id].data_edge,
+            msdc_cap[host->host_id].cmd_edge,
+            (u4buswidth == 0) ?
+            "1-bit" : (u4buswidth == 1) ?
+            "4-bits" : (u4buswidth == 2) ?
+            "8-bits" : "undefined");
+}
+
+void msdc_config_bus(struct mmc_host *host, u32 width)
+{
+    u32 val,mode, div;
+    addr_t base = host->base;
+
+    val = (width == HOST_BUS_WIDTH_8) ? 2 :
+          (width == HOST_BUS_WIDTH_4) ? 1 : 0;
+
+    MSDC_SET_FIELD(SDC_CFG, SDC_CFG_BUSWIDTH, val);
+    MSDC_GET_FIELD(MSDC_CFG,MSDC_CFG_CKMOD,mode);
+    MSDC_GET_FIELD(MSDC_CFG,MSDC_CFG_CKDIV,div);
+
+    LTRACEF("CLK (%dMHz), SCLK(%dkHz) MODE(%d) DIV(%d) buswidth(%u-bits)\n",
+            host->clk/1000000, host->sclk/1000, mode, div, width);
+}
+
+static void msdc_config_pin(struct mmc_host *host, int mode)
+{
+    LTRACEF("[MSDC] Pins mode(%d), none(0), down(1), up(2), keep(3)\n", mode);
+
+    switch (mode) {
+        case MSDC_PIN_PULL_UP:
+            msdc_pin_set(host, 0, MSDC_10KOHM);
+            break;
+        case MSDC_PIN_PULL_DOWN:
+            msdc_pin_set(host, 1, MSDC_50KOHM);
+            break;
+        case MSDC_PIN_PULL_NONE:
+        default:
+            msdc_pin_set(host, 1, MSDC_50KOHM);
+            break;
+    }
+}
+
+void msdc_clock(struct mmc_host *host, int on)
+{
+    /* Chaotian, may need update this part of code */
+    LTRACEF("[MSDC] Turn %s %s clock \n", on ? "on" : "off", "host");
+}
+
+static void msdc_host_power(struct mmc_host *host, int on)
+{
+    LTRACEF("[MSDC] Turn %s %s power \n", on ? "on" : "off", "host");
+    return; /* power always on, return directly */
+
+    if (on) {
+        msdc_config_pin(host, MSDC_PIN_PULL_UP);
+        msdc_set_host_pwr(host, 1);
+        msdc_clock(host, 1);
+    } else {
+        msdc_clock(host, 0);
+        msdc_set_host_pwr(host, 0);
+        msdc_config_pin(host, MSDC_PIN_PULL_DOWN);
+    }
+}
+
+static void msdc_card_power(struct mmc_host *host, int on)
+{
+    LTRACEF("[MSDC] Turn %s %s power \n", on ? "on" : "off", "card");
+    return; /* power always on, return directly */
+
+    if (on) {
+        msdc_set_card_pwr(host, 1);
+    } else {
+        msdc_set_card_pwr(host, 0);
+    }
+}
+
+void msdc_power(struct mmc_host *host, u8 mode)
+{
+    if (mode == MMC_POWER_ON || mode == MMC_POWER_UP) {
+        msdc_host_power(host, 1);
+        msdc_card_power(host, 1);
+    } else {
+        msdc_card_power(host, 0);
+        msdc_host_power(host, 0);
+    }
+}
+
+void msdc_reset_tune_counter(struct mmc_host *host)
+{
+    host->time_read = 0;
+}
+
+#ifdef FEATURE_MMC_CM_TUNING
+static int msdc_tune_cmdrsp(struct mmc_host *host, struct mmc_command *cmd)
+{
+    addr_t base = host->base;
+    u32 sel = 0;
+    u32 rsmpl,cur_rsmpl, orig_rsmpl;
+    u32 rrdly,cur_rrdly, orig_rrdly;
+    u32 cntr,cur_cntr,orig_cmdrtc;
+    u32 dl_cksel, cur_dl_cksel, orig_dl_cksel;
+    u32 times = 0;
+    int result = MMC_ERR_CMDTUNEFAIL;
+    u32 tmp = 0;
+
+    if (host->sclk > 100000000) {
+        sel = 1;
+        //MSDC_SET_FIELD(MSDC_PATCH_BIT0, MSDC_CKGEN_RX_SDCLKO_SEL,0);
+    }
+
+    MSDC_GET_FIELD(MSDC_IOCON, MSDC_IOCON_RSPL, orig_rsmpl);
+    MSDC_GET_FIELD(MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRDLY, orig_rrdly);
+    MSDC_GET_FIELD(MSDC_PATCH_BIT1, MSDC_PATCH_BIT1_CMD_RSP, orig_cmdrtc);
+    MSDC_GET_FIELD(MSDC_PATCH_BIT0, MSDC_INT_DAT_LATCH_CK_SEL, orig_dl_cksel);
+
+    dl_cksel = 0;
+    do {
+        cntr = 0;
+        do {
+            rrdly = 0;
+            do {
+                for (rsmpl = 0; rsmpl < 2; rsmpl++) {
+                    cur_rsmpl = (orig_rsmpl + rsmpl) % 2;
+                    MSDC_SET_FIELD(MSDC_IOCON, MSDC_IOCON_RSPL, cur_rsmpl);
+                    if (host->sclk <= 400000) { //In sd/emmc init flow, fix rising edge for latching cmd response
+                        MSDC_SET_FIELD(MSDC_IOCON, MSDC_IOCON_RSPL, 0);
+                    }
+                    if (cmd->opcode != MMC_CMD_STOP_TRANSMISSION) {
+                        result = msdc_send_cmd(host, cmd);
+                        if (result == MMC_ERR_TIMEOUT)
+                            rsmpl--;
+                        if (result != MMC_ERR_NONE && cmd->opcode != MMC_CMD_STOP_TRANSMISSION) {
+                            if (cmd->opcode == MMC_CMD_READ_MULTIPLE_BLOCK || cmd->opcode == MMC_CMD_WRITE_MULTIPLE_BLOCK || cmd->opcode == MMC_CMD_READ_SINGLE_BLOCK ||cmd->opcode == MMC_CMD_WRITE_BLOCK)
+                                msdc_abort_handler(host,1);
+                            continue;
+                        }
+                        result = msdc_wait_cmd_done(host, cmd);
+                    } else if (cmd->opcode == MMC_CMD_STOP_TRANSMISSION) {
+                        result = MMC_ERR_NONE;
+                        goto done;
+                    } else
+                        result = MMC_ERR_BADCRC;
+
+                    times++;
+                    if (result == MMC_ERR_NONE)
+                        goto done;
+                    tmp = MSDC_READ32(SDC_CMD);
+                    /* check if data is used by the command or not */
+                    if (tmp & 0x1800) {
+                        if (msdc_abort_handler(host, 1))
+                            dprintf(CRITICAL, "[MSDC] abort failed\n");
+                    }
+                }
+                cur_rrdly = (orig_rrdly + rrdly + 1) % 32;
+                MSDC_SET_FIELD(MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRDLY, cur_rrdly);
+            } while (++rrdly < 32);
+            if (!sel)
+                break;
+            cur_cntr = (orig_cmdrtc + cntr + 1) % 8;
+            MSDC_SET_FIELD(MSDC_PATCH_BIT1, MSDC_PATCH_BIT1_CMD_RSP, cur_cntr);
+        } while (++cntr < 8);
+        /* no need to update data ck sel */
+        if (!sel)
+            break;
+        cur_dl_cksel = (orig_dl_cksel +dl_cksel+1) % 8;
+        MSDC_SET_FIELD(MSDC_PATCH_BIT0, MSDC_INT_DAT_LATCH_CK_SEL, cur_dl_cksel);
+        dl_cksel++;
+    } while (dl_cksel < 8);
+    /* no need to update ck sel */
+    if (result != MMC_ERR_NONE)
+        result = MMC_ERR_CMDTUNEFAIL;
+done:
+
+    dprintf(ALWAYS, "[MSDC] <TUNE_CMD><%d><%s>\n", times, (result == MMC_ERR_NONE) ?"PASS" : "FAIL");
+    return result;
+}
+#endif
+
+#ifdef FEATURE_MMC_RD_TUNING
+int msdc_tune_bread(struct mmc_host *host, u8 *dst, u32 src, u32 nblks)
+{
+    addr_t base = host->base;
+    u32 dcrc = 1, ddr = 0, sel = 0;
+    u32 cur_rxdly0, cur_rxdly1;
+    u32 dsmpl, cur_dsmpl, orig_dsmpl;
+    u32 dsel,cur_dsel,orig_dsel;
+    u32 dl_cksel,cur_dl_cksel,orig_dl_cksel;
+    u32 rxdly;
+    u32 cur_dat0, cur_dat1, cur_dat2, cur_dat3, cur_dat4, cur_dat5,
+        cur_dat6, cur_dat7;
+    u32 orig_dat0, orig_dat1, orig_dat2, orig_dat3, orig_dat4, orig_dat5,
+        orig_dat6, orig_dat7;
+    u32 orig_clkmode;
+    u32 times = 0;
+    int result = MMC_ERR_READTUNEFAIL;
+
+    if (host->sclk > 100000000)
+        sel = 1;
+    if (host->card)
+        ddr = mmc_card_ddr(host->card);
+    MSDC_GET_FIELD(MSDC_CFG,MSDC_CFG_CKMOD,orig_clkmode);
+    MSDC_GET_FIELD(MSDC_PATCH_BIT0, MSDC_CKGEN_MSDC_DLY_SEL, orig_dsel);
+    MSDC_GET_FIELD(MSDC_PATCH_BIT0, MSDC_INT_DAT_LATCH_CK_SEL, orig_dl_cksel);
+    MSDC_GET_FIELD(MSDC_IOCON, MSDC_IOCON_DSPL, orig_dsmpl);
+
+    /* Tune Method 2. delay each data line */
+    MSDC_SET_FIELD(MSDC_IOCON, MSDC_IOCON_DDLSEL, 1);
+
+    dl_cksel = 0;
+    do {
+        dsel = 0;
+        do {
+            rxdly = 0;
+            do {
+                for (dsmpl = 0; dsmpl < 2; dsmpl++) {
+                    cur_dsmpl = (orig_dsmpl + dsmpl) % 2;
+                    MSDC_SET_FIELD(MSDC_IOCON, MSDC_IOCON_DSPL, cur_dsmpl);
+                    result = host->blk_read(host, dst, src, nblks);
+                    if (result == MMC_ERR_CMDTUNEFAIL || result == MMC_ERR_CMD_RSPCRC || result == MMC_ERR_ACMD_RSPCRC)
+                        goto done;
+                    MSDC_GET_FIELD(SDC_DCRC_STS, SDC_DCRC_STS_POS|SDC_DCRC_STS_NEG, dcrc);
+
+                    if (!ddr)
+                        dcrc &= ~SDC_DCRC_STS_NEG;
+
+                    /* for debugging */
+                    times++;
+                    /* no cre error in this data line */
+                    if (result == MMC_ERR_NONE && dcrc == 0) {
+                        goto done;
+                    } else {
+                        result = MMC_ERR_BADCRC;
+                    }
+                }
+                cur_rxdly0 = MSDC_READ32(MSDC_DAT_RDDLY0);
+                cur_rxdly1 = MSDC_READ32(MSDC_DAT_RDDLY1);
+
+
+                orig_dat0 = (cur_rxdly0 >> 24) & 0x1F;
+                orig_dat1 = (cur_rxdly0 >> 16) & 0x1F;
+                orig_dat2 = (cur_rxdly0 >> 8) & 0x1F;
+                orig_dat3 = (cur_rxdly0 >> 0) & 0x1F;
+                orig_dat4 = (cur_rxdly1 >> 24) & 0x1F;
+                orig_dat5 = (cur_rxdly1 >> 16) & 0x1F;
+                orig_dat6 = (cur_rxdly1 >> 8) & 0x1F;
+                orig_dat7 = (cur_rxdly1 >> 0) & 0x1F;
+
+                if (ddr) {
+                    cur_dat0 = (dcrc & (1 << 0) || dcrc & (1 <<  8)) ? ((orig_dat0 + 1) % 32) : orig_dat0;
+                    cur_dat1 = (dcrc & (1 << 1) || dcrc & (1 <<  9)) ? ((orig_dat1 + 1) % 32) : orig_dat1;
+                    cur_dat2 = (dcrc & (1 << 2) || dcrc & (1 << 10)) ? ((orig_dat2 + 1) % 32) : orig_dat2;
+                    cur_dat3 = (dcrc & (1 << 3) || dcrc & (1 << 11)) ? ((orig_dat3 + 1) % 32) : orig_dat3;
+                    cur_dat4 = (dcrc & (1 << 4) || dcrc & (1 << 12)) ? ((orig_dat4 + 1) % 32) : orig_dat4;
+                    cur_dat5 = (dcrc & (1 << 5) || dcrc & (1 << 13)) ? ((orig_dat5 + 1) % 32) : orig_dat5;
+                    cur_dat6 = (dcrc & (1 << 6) || dcrc & (1 << 14)) ? ((orig_dat6 + 1) % 32) : orig_dat6;
+                    cur_dat7 = (dcrc & (1 << 7) || dcrc & (1 << 15)) ? ((orig_dat7 + 1) % 32) : orig_dat7;
+                } else {
+                    cur_dat0 = (dcrc & (1 << 0)) ? ((orig_dat0 + 1) % 32) : orig_dat0;
+                    cur_dat1 = (dcrc & (1 << 1)) ? ((orig_dat1 + 1) % 32) : orig_dat1;
+                    cur_dat2 = (dcrc & (1 << 2)) ? ((orig_dat2 + 1) % 32) : orig_dat2;
+                    cur_dat3 = (dcrc & (1 << 3)) ? ((orig_dat3 + 1) % 32) : orig_dat3;
+                    cur_dat4 = (dcrc & (1 << 4)) ? ((orig_dat4 + 1) % 32) : orig_dat4;
+                    cur_dat5 = (dcrc & (1 << 5)) ? ((orig_dat5 + 1) % 32) : orig_dat5;
+                    cur_dat6 = (dcrc & (1 << 6)) ? ((orig_dat6 + 1) % 32) : orig_dat6;
+                    cur_dat7 = (dcrc & (1 << 7)) ? ((orig_dat7 + 1) % 32) : orig_dat7;
+                }
+
+                cur_rxdly0 = ((cur_dat0 & 0x1F) << 24) | ((cur_dat1 & 0x1F) << 16) |
+                             ((cur_dat2 & 0x1F)<< 8) | ((cur_dat3 & 0x1F) << 0);
+                cur_rxdly1 = ((cur_dat4 & 0x1F) << 24) | ((cur_dat5 & 0x1F) << 16) |
+                             ((cur_dat6 & 0x1F) << 8) | ((cur_dat7 & 0x1F) << 0);
+
+                MSDC_WRITE32(MSDC_DAT_RDDLY0, cur_rxdly0);
+                MSDC_WRITE32(MSDC_DAT_RDDLY1, cur_rxdly1);
+            } while (++rxdly < 32);
+            if (!sel)
+                break;
+            cur_dsel = (orig_dsel + dsel + 1) % 32;
+            MSDC_SET_FIELD(MSDC_PATCH_BIT0, MSDC_CKGEN_MSDC_DLY_SEL, cur_dsel);
+        } while (++dsel < 32);
+        /* no need to update data ck sel */
+        if (orig_clkmode != 1)
+            break;
+
+        cur_dl_cksel = (orig_dl_cksel + dl_cksel + 1)% 8;
+        MSDC_SET_FIELD(MSDC_PATCH_BIT0, MSDC_INT_DAT_LATCH_CK_SEL, cur_dl_cksel);
+        dl_cksel++;
+    } while (dl_cksel < 8);
+done:
+    dprintf(ALWAYS, "[MSDC] <msdc_tune_bread<%d><%s><cmd%d>@msdc_tune_bread\n",
+            times, (result == MMC_ERR_NONE && dcrc == 0) ? "PASS" : "FAIL",
+            (nblks == 1 ? 17 : 18));
+
+    return result;
+}
+#define READ_TUNING_MAX_HS (2 * 32)
+#define READ_TUNING_MAX_UHS (2 * 32 * 32)
+#define READ_TUNING_MAX_UHS_CLKMOD1 (2 * 32 * 32 *8)
+
+int msdc_tune_read(struct mmc_host *host)
+{
+    return 0;
+#if 0
+    addr_t base = host->base;
+    u32 dcrc, ddr = 0, sel = 0;
+    u32 cur_rxdly0 = 0 , cur_rxdly1 = 0;
+    u32 cur_dsmpl = 0, orig_dsmpl;
+    u32 cur_dsel = 0,orig_dsel;
+    u32 cur_dl_cksel = 0,orig_dl_cksel;
+    u32 cur_dat0 = 0, cur_dat1 = 0, cur_dat2 = 0, cur_dat3 = 0, cur_dat4 = 0, cur_dat5 = 0,
+        cur_dat6 = 0, cur_dat7 = 0;
+    u32 orig_dat0, orig_dat1, orig_dat2, orig_dat3, orig_dat4, orig_dat5,
+        orig_dat6, orig_dat7;
+    u32 orig_clkmode;
+    u32 times = 0;
+    int result = MMC_ERR_NONE;
+
+    if (host->sclk > 100000000)
+        sel = 1;
+    if (host->card)
+        ddr = mmc_card_ddr(host->card);
+    MSDC_GET_FIELD(MSDC_CFG,MSDC_CFG_CKMOD,orig_clkmode);
+    //if(orig_clkmode == 1)
+    //MSDC_SET_FIELD(MSDC_PATCH_BIT0, MSDC_CKGEN_RX_SDCLKO_SEL, 0);
+
+    MSDC_GET_FIELD(MSDC_PATCH_BIT0, MSDC_CKGEN_MSDC_DLY_SEL, orig_dsel);
+    MSDC_GET_FIELD(MSDC_PATCH_BIT0, MSDC_INT_DAT_LATCH_CK_SEL, orig_dl_cksel);
+    MSDC_GET_FIELD(MSDC_IOCON, MSDC_IOCON_DSPL, orig_dsmpl);
+
+    /* Tune Method 2. delay each data line */
+    MSDC_SET_FIELD(MSDC_IOCON, MSDC_IOCON_DDLSEL, 1);
+
+
+    cur_dsmpl = (orig_dsmpl + 1) ;
+    MSDC_SET_FIELD(MSDC_IOCON, MSDC_IOCON_DSPL, cur_dsmpl % 2);
+    if (cur_dsmpl >= 2) {
+        MSDC_GET_FIELD(SDC_DCRC_STS, SDC_DCRC_STS_POS|SDC_DCRC_STS_NEG, dcrc);
+        if (!ddr) dcrc &= ~SDC_DCRC_STS_NEG;
+
+        cur_rxdly0 = MSDC_READ32(MSDC_DAT_RDDLY0);
+        cur_rxdly1 = MSDC_READ32(MSDC_DAT_RDDLY1);
+
+        orig_dat0 = (cur_rxdly0 >> 24) & 0x1F;
+        orig_dat1 = (cur_rxdly0 >> 16) & 0x1F;
+        orig_dat2 = (cur_rxdly0 >> 8) & 0x1F;
+        orig_dat3 = (cur_rxdly0 >> 0) & 0x1F;
+        orig_dat4 = (cur_rxdly1 >> 24) & 0x1F;
+        orig_dat5 = (cur_rxdly1 >> 16) & 0x1F;
+        orig_dat6 = (cur_rxdly1 >> 8) & 0x1F;
+        orig_dat7 = (cur_rxdly1 >> 0) & 0x1F;
+
+        if (ddr) {
+            cur_dat0 = (dcrc & (1 << 0) || dcrc & (1 <<  8)) ? (orig_dat0 + 1) : orig_dat0;
+            cur_dat1 = (dcrc & (1 << 1) || dcrc & (1 <<  9)) ? (orig_dat1 + 1) : orig_dat1;
+            cur_dat2 = (dcrc & (1 << 2) || dcrc & (1 << 10)) ? (orig_dat2 + 1) : orig_dat2;
+            cur_dat3 = (dcrc & (1 << 3) || dcrc & (1 << 11)) ? (orig_dat3 + 1) : orig_dat3;
+            cur_dat4 = (dcrc & (1 << 4) || dcrc & (1 << 12)) ? (orig_dat4 + 1) : orig_dat4;
+            cur_dat5 = (dcrc & (1 << 5) || dcrc & (1 << 13)) ? (orig_dat5 + 1) : orig_dat5;
+            cur_dat6 = (dcrc & (1 << 6) || dcrc & (1 << 14)) ? (orig_dat6 + 1) : orig_dat6;
+            cur_dat7 = (dcrc & (1 << 7) || dcrc & (1 << 15)) ? (orig_dat7 + 1) : orig_dat7;
+        } else {
+            cur_dat0 = (dcrc & (1 << 0)) ? (orig_dat0 + 1) : orig_dat0;
+            cur_dat1 = (dcrc & (1 << 1)) ? (orig_dat1 + 1) : orig_dat1;
+            cur_dat2 = (dcrc & (1 << 2)) ? (orig_dat2 + 1) : orig_dat2;
+            cur_dat3 = (dcrc & (1 << 3)) ? (orig_dat3 + 1) : orig_dat3;
+            cur_dat4 = (dcrc & (1 << 4)) ? (orig_dat4 + 1) : orig_dat4;
+            cur_dat5 = (dcrc & (1 << 5)) ? (orig_dat5 + 1) : orig_dat5;
+            cur_dat6 = (dcrc & (1 << 6)) ? (orig_dat6 + 1) : orig_dat6;
+            cur_dat7 = (dcrc & (1 << 7)) ? (orig_dat7 + 1) : orig_dat7;
+        }
+
+        cur_rxdly0 = ((cur_dat0 & 0x1F) << 24) | ((cur_dat1 & 0x1F) << 16) |
+                     ((cur_dat2 & 0x1F) << 8) | ((cur_dat3 & 0x1F) << 0);
+        cur_rxdly1 = ((cur_dat4 & 0x1F) << 24) | ((cur_dat5 & 0x1F)<< 16) |
+                     ((cur_dat6 & 0x1F) << 8) | ((cur_dat7 & 0x1F) << 0);
+
+        MSDC_WRITE32(MSDC_DAT_RDDLY0, cur_rxdly0);
+        MSDC_WRITE32(MSDC_DAT_RDDLY1, cur_rxdly1);
+    }
+    if (cur_dat0 >= 32 || cur_dat1 >= 32 || cur_dat2 >= 32 || cur_dat3 >= 32 ||
+            cur_dat4 >= 32 || cur_dat5 >= 32 || cur_dat6 >= 32 || cur_dat7 >= 32) {
+        if (sel) {
+
+            cur_dsel = (orig_dsel + 1);
+            MSDC_SET_FIELD(MSDC_PATCH_BIT0, MSDC_CKGEN_MSDC_DLY_SEL, cur_dsel % 32);
+
+        }
+    }
+    if (cur_dsel >= 32) {
+        if (orig_clkmode == 1 && sel) {
+
+            cur_dl_cksel = (orig_dl_cksel + 1);
+            MSDC_SET_FIELD(MSDC_PATCH_BIT0, MSDC_INT_DAT_LATCH_CK_SEL, cur_dl_cksel % 8);
+        }
+    }
+    ++(host->time_read);
+    if ((sel == 1 && orig_clkmode == 1 && host->time_read == READ_TUNING_MAX_UHS_CLKMOD1)||
+            (sel == 1 && orig_clkmode != 1 && host->time_read == READ_TUNING_MAX_UHS)||
+            (sel == 0 && orig_clkmode != 1 && host->time_read == READ_TUNING_MAX_HS)) {
+
+        result = MMC_ERR_READTUNEFAIL;
+    }
+
+    return result;
+#endif
+}
+#endif
+
+#ifdef FEATURE_MMC_WR_TUNING
+int msdc_tune_bwrite(struct mmc_host *host, u32 dst, u8 *src, u32 nblks)
+{
+    addr_t base = host->base;
+    u32 sel = 0;
+    u32 wrrdly, cur_wrrdly, orig_wrrdly;
+    u32 dsmpl, cur_dsmpl, orig_dsmpl;
+    u32 d_cntr,orig_d_cntr,cur_d_cntr;
+    u32 rxdly, cur_rxdly0;
+    u32 orig_dat0, orig_dat1, orig_dat2, orig_dat3;
+    u32 cur_dat0, cur_dat1, cur_dat2, cur_dat3;
+    u32 times = 0;
+    int result = MMC_ERR_WRITETUNEFAIL;
+
+    if (host->sclk > 100000000)
+        sel = 1;
+
+    MSDC_GET_FIELD(MSDC_PAD_TUNE, MSDC_PAD_TUNE_DATWRDLY, orig_wrrdly);
+    MSDC_GET_FIELD(MSDC_IOCON, MSDC_IOCON_W_D_SMPL, orig_dsmpl);
+    MSDC_GET_FIELD(MSDC_PATCH_BIT1, MSDC_PATCH_BIT1_WRDAT_CRCS, orig_d_cntr);
+
+    /* Tune Method 2. delay data0 line */
+    MSDC_SET_FIELD(MSDC_IOCON, MSDC_IOCON_DDLSEL, 1);
+
+    cur_rxdly0 = MSDC_READ32(MSDC_DAT_RDDLY0);
+
+    orig_dat0 = (cur_rxdly0 >> 24) & 0x1F;
+    orig_dat1 = (cur_rxdly0 >> 16) & 0x1F;
+    orig_dat2 = (cur_rxdly0 >> 8) & 0x1F;
+    orig_dat3 = (cur_rxdly0 >> 0) & 0x1F;
+
+    d_cntr = 0;
+    do {
+        rxdly = 0;
+        do {
+            wrrdly = 0;
+            do {
+                for (dsmpl = 0; dsmpl < 2; dsmpl++) {
+                    cur_dsmpl = (orig_dsmpl + dsmpl) % 2;
+                    MSDC_SET_FIELD(MSDC_IOCON, MSDC_IOCON_W_D_SMPL, cur_dsmpl);
+                    result = host->blk_write(host, dst, src, nblks);
+                    if (result == MMC_ERR_CMDTUNEFAIL || result == MMC_ERR_CMD_RSPCRC || result == MMC_ERR_ACMD_RSPCRC)
+                        goto done;
+
+                    times++;
+                    if (result == MMC_ERR_NONE) {
+                        goto done;
+                    }
+                }
+                cur_wrrdly = ++orig_wrrdly % 32;
+                MSDC_SET_FIELD(MSDC_PAD_TUNE, MSDC_PAD_TUNE_DATWRDLY, cur_wrrdly);
+            } while (++wrrdly < 32);
+
+            cur_dat0 = ++orig_dat0 % 32; /* only adjust bit-1 for crc */
+            cur_dat1 = orig_dat1;
+            cur_dat2 = orig_dat2;
+            cur_dat3 = orig_dat3;
+
+            cur_rxdly0 = (cur_dat0 << 24) | (cur_dat1 << 16) |
+                         (cur_dat2 << 8) | (cur_dat3 << 0);
+
+            MSDC_WRITE32(MSDC_DAT_RDDLY0, cur_rxdly0);
+        } while (++rxdly < 32);
+
+        /* no need to update data ck sel */
+        if (!sel)
+            break;
+
+        cur_d_cntr= (orig_d_cntr + d_cntr +1 )% 8;
+        MSDC_SET_FIELD(MSDC_PATCH_BIT1, MSDC_PATCH_BIT1_WRDAT_CRCS, cur_d_cntr);
+        d_cntr++;
+    } while (d_cntr < 8);
+done:
+    dprintf(ALWAYS, "[MSDC] <TUNE_BWRITE_%d><%s>\n", times, result == MMC_ERR_NONE ? "PASS" : "FAIL");
+
+    return result;
+}
+#endif
+
+void msdc_emmc_boot_stop(struct mmc_host *host)
+{
+    addr_t base = host->base;
+    u32 count = 0;
+
+    /* Step5. stop the boot mode */
+    MSDC_WRITE32(SDC_ARG, 0x00000000);
+    MSDC_WRITE32(SDC_CMD, 0x00001000);
+
+    MSDC_SET_FIELD(EMMC_CFG0, EMMC_CFG0_BOOTWDLY, 2);
+    MSDC_SET_BIT32(EMMC_CFG0, EMMC_CFG0_BOOTSTOP);
+    while (MSDC_READ32(EMMC_STS) & EMMC_STS_BOOTUPSTATE) {
+        spin(1000);
+        count++;
+        if (count >= 1000) {
+            dprintf(ALWAYS, "Timeout to wait EMMC to leave boot state!\n");
+            break;
+        }
+    }
+
+    /* Step6. */
+    MSDC_CLR_BIT32(EMMC_CFG0, EMMC_CFG0_BOOTSUPP);
+
+    /* Step7. clear EMMC_STS bits */
+    MSDC_WRITE32(EMMC_STS, MSDC_READ32(EMMC_STS));
+}
+
+int msdc_init(struct mmc_host *host)
+{
+    addr_t base = host->host_id ? MSDC1_BASE: MSDC0_BASE; /* only support MSDC0, MSDC1 */
+    msdc_priv_t *priv;
+
+    LTRACEF("[%s]: Host controller intialization start \n", __func__);
+
+    priv = &msdc_priv;
+    memset(priv, 0, sizeof(msdc_priv_t));
+
+    host->base   = base;
+    host->clksrc = msdc_cap[host->host_id].clk_src;
+    host->hclksrc= msdc_cap[host->host_id].hclk_src;
+#ifndef FPGA_PLATFORM
+    host->f_max  = hclks_msdc30[host->clksrc];
+#else
+    host->f_max  = MSDC_MAX_SCLK;
+#endif
+
+    host->f_min  = MSDC_MIN_SCLK;
+    host->blklen = 0;
+    host->priv   = (void *)priv;
+    host->caps   = MMC_CAP_MULTIWRITE;
+
+    if (msdc_cap[host->host_id].flags & MSDC_HIGHSPEED)
+        host->caps |= (MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED);
+    if (msdc_cap[host->host_id].flags & MSDC_DDR)
+        host->caps |= MMC_CAP_DDR;
+    if (msdc_cap[host->host_id].data_pins == 4)
+        host->caps |= MMC_CAP_4_BIT_DATA;
+    if (msdc_cap[host->host_id].data_pins == 8)
+        host->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA;
+    if (msdc_cap[host->host_id].flags & MSDC_HS200)
+        host->caps |= MMC_CAP_EMMC_HS200;
+    if (msdc_cap[host->host_id].flags & MSDC_HS400)
+        host->caps |= MMC_CAP_EMMC_HS400;
+
+    host->ocr_avail = MMC_VDD_32_33;  /* TODO: To be customized */
+
+    /* Configure BASIC_DMA + AUTOCMD12 for better R/W performance
+     * NOTE: ACMD23 only support transferring size of up to 32M */
+    priv->autocmd = MSDC_AUTOCMD12;
+    if (priv->autocmd == MSDC_AUTOCMD23)
+        /* The maximal transferring size is size of *[15:0] number of blocks* */
+        host->max_phys_segs = 0xffff;
+    else
+        /* The maximal transferring size is size of DMA_LENGTH */
+        host->max_phys_segs = (UINT_MAX & ~511) >> MMC_BLOCK_BITS_SHFT;
+
+    priv->rdsmpl       = msdc_cap[host->host_id].data_edge;
+    priv->wdsmpl       = msdc_cap[host->host_id].data_edge;
+    priv->rsmpl       = msdc_cap[host->host_id].cmd_edge;
+
+#ifdef MSDC_USE_DMA_MODE
+    host->blk_read  = msdc_dma_bread;
+    host->blk_write = msdc_dma_bwrite;
+    LTRACEF("Transfer method: DMA\n");
+#else
+    host->blk_read  = msdc_pio_bread;
+    host->blk_write = msdc_pio_bwrite;
+    LTRACEF("Transfer method: PIO\n");
+#endif
+
+    priv->rdsmpl       = msdc_cap[host->host_id].data_edge;
+    priv->rsmpl       = msdc_cap[host->host_id].cmd_edge;
+
+    if (host->skip_reinit)
+        goto skip_host_init;
+    /* disable EMMC boot mode */
+    msdc_emmc_boot_stop(host);
+
+    msdc_power(host, MMC_POWER_OFF);
+    msdc_power(host, MMC_POWER_ON);
+
+    if (host->host_id == 1)
+        sd_card_vccq_on();
+
+    /* set to SD/MMC mode */
+    MSDC_SET_FIELD(MSDC_CFG, MSDC_CFG_MODE, MSDC_SDMMC);
+    MSDC_SET_BIT32(MSDC_CFG, MSDC_CFG_PIO);
+    MSDC_SET_BIT32(MSDC_CFG, MSDC_CFG_CKPDN);
+
+    MSDC_RESET();
+    MSDC_CLR_FIFO();
+    MSDC_CLR_INT();
+
+    /* reset tuning parameter */
+    //MSDC_WRITE32(MSDC_PAD_CTL0, 0x0098000);
+    //MSDC_WRITE32(MSDC_PAD_CTL1, 0x00A0000);
+    //MSDC_WRITE32(MSDC_PAD_CTL2, 0x00A0000);
+    MSDC_WRITE32(MSDC_PAD_TUNE, 0x0000000);
+    MSDC_WRITE32(MSDC_DAT_RDDLY0, 0x00000000);
+    MSDC_WRITE32(MSDC_DAT_RDDLY1, 0x00000000);
+    MSDC_WRITE32(MSDC_IOCON, 0x00000000);
+
+    MSDC_SET_BIT32(MSDC_PAD_TUNE, MSDC_PAD_TUNE_DATRRDLYSEL);
+    MSDC_SET_BIT32(MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRDLYSEL);
+    MSDC_WRITE32(MSDC_PATCH_BIT0, 0x403c0046);
+    MSDC_WRITE32(MSDC_PATCH_BIT1, 0xFFFF4309);//High 16 bit = 0 mean Power KPI is on, enable ECO for write timeout issue
+    MSDC_SET_BIT32(EMMC50_CFG0, MSDC_EMMC50_CFG_CRCSTS_SEL);
+    MSDC_CLR_BIT32(SDC_FIFO_CFG, SDC_FIFO_CFG_WRVALIDSEL);
+    MSDC_CLR_BIT32(SDC_FIFO_CFG, SDC_FIFO_CFG_RDVALIDSEL);
+    MSDC_SET_BIT32(SDC_AVG_CFG0, SDC_RX_ENHANCE_EN);
+    MSDC_SET_BIT32(MSDC_PATCH_BIT2, MSDC_PB2_CFGCRCSTS);
+    MSDC_CLR_BIT32(MSDC_PATCH_BIT1, MSDC_BUSY_CHECK_SEL); /* disable busy check */
+    //MSDC_PATCH_BIT1YD:WRDAT_CRCS_TA_CNTR need fix to 3'001 by default,(<50MHz) (>=50MHz set 3'001 as initial value is OK for tunning)
+    //YD:CMD_RSP_TA_CNTR need fix to 3'001 by default(<50MHz)(>=50MHz set 3'001as initial value is OK for tunning)
+    /* 2012-01-07 using internal clock instead of feedback clock */
+    //MSDC_SET_BIT32(MSDC_PATCH_BIT0, MSDC_CKGEN_MSDC_CK_SEL);
+
+#ifdef MSDC_USE_PATCH_BIT2_TURNING_WITH_ASYNC
+    MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_CFGCRCSTS,1);
+    MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_CFGRESP,0);
+#else
+    MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_CFGCRCSTS,0);
+    MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_CFGRESP,1);
+#endif
+
+    /* enable wake up events */
+    //MSDC_SET_BIT32(SDC_CFG, SDC_CFG_INSWKUP);
+
+    /* eneable SMT for glitch filter */
+#ifdef FPGA_PLATFORM
+#if 0
+    MSDC_SET_BIT32(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKSMT);
+    MSDC_SET_BIT32(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDSMT);
+    MSDC_SET_BIT32(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATSMT);
+#endif
+#else
+    msdc_set_smt(host,1);
+#endif
+    /* set clk, cmd, dat pad driving */
+    msdc_set_driving(host, &msdc_cap[host->host_id]);
+
+    /* set sampling edge */
+    MSDC_SET_FIELD(MSDC_IOCON, MSDC_IOCON_RSPL, msdc_cap[host->host_id].cmd_edge);
+    MSDC_SET_FIELD(MSDC_IOCON, MSDC_IOCON_DSPL, msdc_cap[host->host_id].data_edge);
+
+    /* write crc timeout detection */
+    MSDC_SET_FIELD(MSDC_PATCH_BIT0, MSDC_PB0_DETWR_CRCTMO, 1);
+
+    msdc_set_startbit(host, START_AT_RISING);
+
+    msdc_config_bus(host, HOST_BUS_WIDTH_1);
+    msdc_config_clock(host, 0, MSDC_MIN_SCLK);
+    msdc_set_timeout(host, 100000000, 0);
+
+    /* disable SDIO func */
+    MSDC_SET_FIELD(SDC_CFG, SDC_CFG_SDIO, 0);
+    MSDC_SET_FIELD(SDC_CFG, SDC_CFG_SDIOIDE, 0);
+    MSDC_SET_FIELD(SDC_CFG, SDC_CFG_INSWKUP, 0);
+
+    /* Clear all interrupts first */
+    MSDC_CLR_INT();
+    MSDC_WRITE32(MSDC_INTEN, 0);
+
+    if (host->host_id == 0) {
+	/*
+	 * some Samsung eMMC will occur CMD8 data CRC error
+	 * because eMMC card still in boot mode, even send CMD0
+	 * to eMMC. so add HW reset before CMD0.
+	 */
+	MSDC_SET_BIT32(EMMC_IOCON, EMMC_IOCON_BOOTRST);
+	spin(10);
+	MSDC_CLR_BIT32(EMMC_IOCON, EMMC_IOCON_BOOTRST);
+    }
+
+skip_host_init:
+    /* set clk, cmd, dat pad driving */
+    if (host->skip_reinit)
+        msdc_set_driving(host, &msdc_cap[host->host_id]);
+#ifdef MSDC_USE_DMA_MODE
+    /* Register msdc irq */
+    mt_irq_set_sens(MSDC0_IRQ_BIT_ID + host->host_id, LEVEL_SENSITIVE);
+    mt_irq_set_polarity(MSDC0_IRQ_BIT_ID + host->host_id, MT65xx_POLARITY_LOW);
+    event_init(&msdc_int_event, false, EVENT_FLAG_AUTOUNSIGNAL);
+    register_int_handler(MSDC0_IRQ_BIT_ID + host->host_id, msdc_interrupt_handler, host);
+    unmask_interrupt(MSDC0_IRQ_BIT_ID + host->host_id);
+#endif
+
+    LTRACEF("[%s]: Host controller intialization done\n", __func__);
+    return 0;
+}
+
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/mmc/rules.mk b/src/bsp/lk/platform/mediatek/mt2712/drivers/mmc/rules.mk
new file mode 100644
index 0000000..44a804b
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/mmc/rules.mk
@@ -0,0 +1,14 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS += \
+    $(LOCAL_DIR)/msdc.c \
+    $(LOCAL_DIR)/mmc_core.c \
+    $(LOCAL_DIR)/mmc_rpmb.c \
+
+MODULE_DEPS += \
+    lib/bio \
+    lib/partition \
+    lib/rpmb \
+
+include make/module.mk
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/nand/mtk_ecc_hal.c b/src/bsp/lk/platform/mediatek/mt2712/drivers/nand/mtk_ecc_hal.c
new file mode 100644
index 0000000..b465e60
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/nand/mtk_ecc_hal.c
@@ -0,0 +1,547 @@
+/*
+ * Copyright (c) 2017 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <arch/ops.h>
+#include <errno.h>
+#include <kernel/event.h>
+#include <kernel/mutex.h>
+#include <kernel/vm.h>
+#include <malloc.h>
+#include <platform/interrupts.h>
+#include <platform/mt_irq.h>
+#include <platform/mt_reg_base.h>
+#include <platform/nand/mtk_ecc_hal.h>
+#include <platform/nand/mtk_nand_common.h>
+#include <reg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+static inline void mtk_ecc_wait_ioready(struct mtk_ecc *ecc)
+{
+    if (!check_with_timeout((readl(ecc->regs +  ECC_PIO_DIRDY) & PIO_DI_RDY), ECC_TIMEOUT))
+        dprintf(CRITICAL, "ecc io not ready\n");
+
+    return;
+}
+
+static void mtk_ecc_runtime_config(struct mtk_ecc *ecc, struct mtk_ecc_config *config)
+{
+    u32 ecc_bit = ECC_CNFG_4BIT, dec_sz, enc_sz;
+    u32 reg;
+
+    switch (config->strength) {
+        case 4:
+            ecc_bit = ECC_CNFG_4BIT;
+            break;
+        case 6:
+            ecc_bit = ECC_CNFG_6BIT;
+            break;
+        case 8:
+            ecc_bit = ECC_CNFG_8BIT;
+            break;
+        case 10:
+            ecc_bit = ECC_CNFG_10BIT;
+            break;
+        case 12:
+            ecc_bit = ECC_CNFG_12BIT;
+            break;
+        case 14:
+            ecc_bit = ECC_CNFG_14BIT;
+            break;
+        case 16:
+            ecc_bit = ECC_CNFG_16BIT;
+            break;
+        case 18:
+            ecc_bit = ECC_CNFG_18BIT;
+            break;
+        case 20:
+            ecc_bit = ECC_CNFG_20BIT;
+            break;
+        case 22:
+            ecc_bit = ECC_CNFG_22BIT;
+            break;
+        case 24:
+            ecc_bit = ECC_CNFG_24BIT;
+            break;
+        case 28:
+            ecc_bit = ECC_CNFG_28BIT;
+            break;
+        case 32:
+            ecc_bit = ECC_CNFG_32BIT;
+            break;
+        case 36:
+            ecc_bit = ECC_CNFG_36BIT;
+            break;
+        case 40:
+            ecc_bit = ECC_CNFG_40BIT;
+            break;
+        case 44:
+            ecc_bit = ECC_CNFG_44BIT;
+            break;
+        case 48:
+            ecc_bit = ECC_CNFG_48BIT;
+            break;
+        case 52:
+            ecc_bit = ECC_CNFG_52BIT;
+            break;
+        case 56:
+            ecc_bit = ECC_CNFG_56BIT;
+            break;
+        case 60:
+            ecc_bit = ECC_CNFG_60BIT;
+            break;
+        case 68:
+            ecc_bit = ECC_CNFG_68BIT;
+            break;
+        case 72:
+            ecc_bit = ECC_CNFG_72BIT;
+            break;
+        case 80:
+            ecc_bit = ECC_CNFG_80BIT;
+            break;
+        default:
+            dprintf(CRITICAL, "invalid strength %d, default to 4 bits\n",
+                    config->strength);
+            break;
+    }
+
+    if (config->op == ECC_ENCODE) {
+        /* configure ECC encoder (in bits) */
+        enc_sz = config->len << 3;
+
+        reg = ecc_bit | (config->mode << ECC_MODE_SHIFT);
+        reg |= (enc_sz << ECC_MS_SHIFT);
+        writel(reg, ecc->regs + ECC_ENCCNFG);
+
+        if (config->mode == ECC_DMA_MODE) {
+            if (config->addr & 0x3)
+                dprintf(CRITICAL, "ecc encode address(0x%x) is not 4B aligned !!\n", config->addr);
+            writel(config->addr, ecc->regs + ECC_ENCDIADDR);
+        }
+
+    } else {
+        /* configure ECC decoder (in bits) */
+        dec_sz = (config->len << 3) + config->strength * ECC_PARITY_BITS;
+
+        reg = ecc_bit | (config->mode << ECC_MODE_SHIFT);
+        reg |= (dec_sz << ECC_MS_SHIFT) | (config->deccon << DEC_CON_SHIFT);
+        reg |= DEC_EMPTY_EN;
+        writel(reg, ecc->regs + ECC_DECCNFG);
+
+        if (config->mode == ECC_DMA_MODE) {
+            if (config->addr & 0x3)
+                dprintf(CRITICAL, "ecc decode address(0x%x) is not 4B aligned !!\n", config->addr);
+            writel(config->addr, ecc->regs + ECC_DECDIADDR);
+        }
+
+        if (config->sectors)
+            ecc->sectors = 1 << (config->sectors - 1);
+    }
+
+    return;
+}
+
+static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc,
+                                     enum mtk_ecc_operation op)
+{
+    if (!check_with_timeout(readl(ecc->regs + ECC_IDLE_REG(op)) & ECC_IDLE_MASK, ECC_TIMEOUT))
+        dprintf(CRITICAL, "%s NOT idle\n", op == ECC_ENCODE ? "encoder" : "decoder");
+
+    return;
+}
+
+static int mtk_ecc_irq_wait(struct mtk_ecc *ecc, lk_time_t timeout)
+{
+    int ret;
+
+    ret = event_wait_timeout(&ecc->irq_event, timeout);
+    if (ret != 0) {
+        dprintf(CRITICAL, "[%s]: failed to get event\n", __func__);
+        return ret;
+    }
+
+    return 0;
+}
+
+static enum handler_return mtk_ecc_interrupt_handler(void *arg)
+{
+    struct mtk_ecc *ecc = arg;
+    enum mtk_ecc_operation op;
+    u32 dec, enc;
+
+    dec = readw(ecc->regs + ECC_DECIRQ_STA) & ECC_IRQ_EN;
+    if (dec) {
+        op = ECC_DECODE;
+        dec = readw(ecc->regs + ECC_DECDONE);
+        if (dec & ecc->sectors) {
+            ecc->sectors = 0;
+            event_signal(&ecc->irq_event, false);
+        } else {
+            return INT_NO_RESCHEDULE;
+        }
+    } else {
+        enc = readl(ecc->regs + ECC_ENCIRQ_STA) & ECC_IRQ_EN;
+        if (enc) {
+            op = ECC_ENCODE;
+            event_signal(&ecc->irq_event, false);
+        } else {
+            return INT_NO_RESCHEDULE;
+        }
+    }
+
+    writel(0, ecc->regs + ECC_IRQ_REG(op));
+
+    return INT_RESCHEDULE;
+}
+
+static int mtk_ecc_request_irq(struct mtk_ecc *ecc)
+{
+    mt_irq_set_sens(NFIECC_IRQ_BIT_ID, LEVEL_SENSITIVE);
+    mt_irq_set_polarity(NFIECC_IRQ_BIT_ID, MT65xx_POLARITY_LOW);
+    event_init(&ecc->irq_event, false, EVENT_FLAG_AUTOUNSIGNAL);
+    register_int_handler(NFIECC_IRQ_BIT_ID, mtk_ecc_interrupt_handler, ecc);
+    unmask_interrupt(NFIECC_IRQ_BIT_ID);
+
+    return 0;
+}
+
+int mtk_ecc_hw_init(struct mtk_ecc **ext_ecc)
+{
+    struct mtk_ecc *ecc;
+
+    ecc = (struct mtk_ecc *)malloc(sizeof(*ecc));
+    if (!ecc)
+        return -ENOMEM;
+
+    memset(ecc, 0, sizeof(*ecc));
+
+    *ext_ecc = ecc;
+
+    ecc->regs = NFIECC_BASE;
+
+    mtk_ecc_wait_idle(ecc, ECC_ENCODE);
+    writew(ECC_OP_DISABLE, ecc->regs + ECC_ENCCON);
+
+    mtk_ecc_wait_idle(ecc, ECC_DECODE);
+    writel(ECC_OP_DISABLE, ecc->regs + ECC_DECCON);
+
+    mutex_init(&ecc->lock);
+
+    /* register interrupt handler */
+    mtk_ecc_request_irq(ecc);
+
+    return 0;
+}
+
+
+int mtk_ecc_enable(struct mtk_ecc *ecc, struct mtk_ecc_config *config, int polling)
+{
+    enum mtk_ecc_operation op = config->op;
+
+    mutex_acquire(&ecc->lock);
+
+    mtk_ecc_wait_idle(ecc, op);
+    mtk_ecc_runtime_config(ecc, config);
+
+    if (!polling) {
+        writew(ECC_IRQ_EN, ecc->regs + ECC_IRQ_REG(op));
+    }
+
+    writew(ECC_OP_ENABLE, ecc->regs + ECC_CTL_REG(op));
+
+    return 0;
+}
+
+void mtk_ecc_disable(struct mtk_ecc *ecc)
+{
+    enum mtk_ecc_operation op = ECC_ENCODE;
+
+    /* find out the running operation */
+    if (readw(ecc->regs + ECC_CTL_REG(op)) != ECC_OP_ENABLE)
+        op = ECC_DECODE;
+
+    /* disable it */
+    mtk_ecc_wait_idle(ecc, op);
+    writew(0, ecc->regs + ECC_IRQ_REG(op));
+    writew(ECC_OP_DISABLE, ecc->regs + ECC_CTL_REG(op));
+
+    mutex_release(&ecc->lock);
+
+    return;
+}
+
+void mtk_ecc_get_stats(struct mtk_ecc *ecc, struct mtk_ecc_stats *stats,
+                       u32 sectors)
+{
+    u32 offset, i, err;
+    u32 bitflips = 0;
+
+    stats->corrected = 0;
+    stats->failed = 0;
+
+    for (i = 0; i < sectors; i++) {
+        offset = (i >> 2);
+        err = readl(ecc->regs + ECC_DECENUM(offset));
+        err = err >> ((i % 4) * 8);
+        err &= ERR_MASK;
+        if (err == ERR_MASK) {
+            /* uncorrectable errors */
+            stats->failed++;
+            dprintf(ALWAYS, "sector %d is uncorrect\n", i);
+            continue;
+        }
+
+        stats->corrected += err;
+        bitflips = MAX(bitflips, err);
+    }
+
+    stats->bitflips = bitflips;
+
+    return;
+}
+
+int mtk_ecc_wait_done(struct mtk_ecc *ecc, enum mtk_ecc_operation op, int polling)
+{
+    int ret = 0;
+
+    if (!polling) {
+        ret = mtk_ecc_irq_wait(ecc, ECC_TIMEOUT);
+        if (!ret) {
+            dprintf(CRITICAL, "mtk_ecc_wait_done timeout\n");
+            return -ETIMEDOUT;
+        }
+    } else {
+        if (op == ECC_ENCODE) {
+            if (!check_with_timeout((readl(ecc->regs + ECC_ENCSTA) & ENC_IDLE), ECC_TIMEOUT)) {
+                dprintf(CRITICAL, "encoder timeout\n");
+                return -ETIMEDOUT;
+            }
+        } else {
+            if (!check_with_timeout((readw(ecc->regs + ECC_DECDONE) & ecc->sectors), ECC_TIMEOUT)) {
+                dprintf(CRITICAL, "decoder timeout\n");
+                return -ETIMEDOUT;
+            }
+        }
+    }
+
+    return 0;
+}
+
+int mtk_ecc_wait_decode_fsm_idle(struct mtk_ecc *ecc)
+{
+    /* decode done does not stands for ecc all work done.
+     * we need check syn, bma, chien, autoc all idle.
+     * just check it when ECC_DECCNFG[13:12] is 3, which means auto correct.*/
+    if (!check_with_timeout(((readl(ecc->regs + ECC_DECFSM) & FSM_MASK) == FSM_IDLE), ECC_TIMEOUT)) {
+        dprintf(CRITICAL, "decode fsm(0x%x) is not idle\n", readl(ecc->regs + ECC_DECFSM));
+        return -ETIMEDOUT;
+    }
+
+    return 0;
+}
+
+int mtk_ecc_encode(struct mtk_ecc *ecc, struct mtk_ecc_config *config,
+                   u8 *data, u32 bytes, int polling)
+{
+    uintptr_t addr;
+    u8 *p;
+    u8 *buf = data;
+    u32 len, i, val = 0;
+    int ret = 0;
+
+    /* encoder memory address should be 4B aligned */
+    if ((config->mode == ECC_DMA_MODE) && ((uintptr_t)buf & 0x3)) {
+        buf = memalign(4, bytes);
+        if (!buf)
+            return -ENOMEM;
+        memcpy(buf, data, bytes);
+    }
+#ifdef WITH_KERNEL_VM
+    addr = (uintptr_t)kvaddr_to_paddr(buf);
+#else
+    addr = (uintptr_t)buf;
+#endif
+    if (config->mode == ECC_DMA_MODE)
+        arch_clean_cache_range((addr_t)buf, (size_t)bytes);
+
+    config->op = ECC_ENCODE;
+    config->addr = (u32)addr;
+    config->len = bytes;
+
+    ret = mtk_ecc_enable(ecc, config, polling);
+    if (ret)
+        goto freebuf;
+
+    if (config->mode == ECC_PIO_MODE) {
+        for (i = 0; i < ((config->len + 3) >> 2); i++) {
+            mtk_ecc_wait_ioready(ecc);
+            writel(*((u32 *)data + i), ecc->regs + ECC_PIO_DI);
+        }
+    }
+
+    ret = mtk_ecc_wait_done(ecc, ECC_ENCODE, polling);
+    if (ret)
+        goto timeout;
+
+    mtk_ecc_wait_idle(ecc, ECC_ENCODE);
+
+    /* Program ECC bytes to OOB: per sector oob = FDM + ECC + SPARE */
+    len = (config->strength * ECC_PARITY_BITS + 7) >> 3;
+    p = data + bytes;
+
+    /* write the parity bytes generated by the ECC back to the OOB region */
+    for (i = 0; i < len; i++) {
+        if ((i % 4) == 0)
+            val = readl(ecc->regs + ECC_ENCPAR(i / 4));
+        p[i] = (val >> ((i % 4) * 8)) & 0xff;
+    }
+
+timeout:
+    mtk_ecc_disable(ecc);
+freebuf:
+    if (config->mode == ECC_DMA_MODE)
+        arch_invalidate_cache_range((addr_t)buf, (size_t)bytes);
+    if (buf != data)
+        free(buf);
+    return ret;
+}
+
+int mtk_ecc_decode(struct mtk_ecc *ecc, struct mtk_ecc_config *config,
+                   u8 *data, u32 len, int polling)
+{
+    struct mtk_ecc_stats stats;
+    u8 *buf = data;
+    uintptr_t addr;
+    u32 decodesize, i;
+    int ret;
+
+    decodesize = len + ((config->strength * ECC_PARITY_BITS + 7) >> 3);
+    if ((decodesize & 0x3)
+            || ((config->mode == ECC_DMA_MODE) && ((uintptr_t)buf & 0x3))) {
+        decodesize += 4 - (decodesize & 0x3);
+        buf = memalign(4, decodesize);
+        if (!buf)
+            return -ENOMEM;
+    }
+    if (config->mode == ECC_DMA_MODE)
+        arch_invalidate_cache_range((addr_t)buf, (size_t)decodesize);
+#ifdef WITH_KERNEL_VM
+    addr = (uintptr_t)kvaddr_to_paddr(buf);
+#else
+    addr = (uintptr_t)buf;
+#endif
+    config->op = ECC_DECODE;
+    config->addr = (u32)addr;
+    config->len = len;
+    ret = mtk_ecc_enable(ecc, config, polling);
+    if (ret)
+        goto freebuf;
+
+    if (config->mode == ECC_PIO_MODE) {
+        for (i = 0; i < (decodesize >> 2); i++) {
+            mtk_ecc_wait_ioready(ecc);
+            writel(*((u32 *)buf + i), ecc->regs + ECC_PIO_DI);
+        }
+    }
+
+    stats.bitflips = 0;
+    ret = mtk_ecc_cpu_correct(ecc, &stats, buf, 0, polling);
+    if (ret)
+        goto disecc;
+
+    if (config->mode == ECC_DMA_MODE)
+        arch_invalidate_cache_range((addr_t)buf, (size_t)decodesize);
+    if (buf != data)
+        memcpy(data, buf, len);
+
+disecc:
+    mtk_ecc_disable(ecc);
+
+freebuf:
+    if (buf != data)
+        free(buf);
+
+    return ret;
+}
+
+int mtk_ecc_cpu_correct(struct mtk_ecc *ecc, struct mtk_ecc_stats *stats, u8 *data, u32 sector, int polling)
+{
+    u32 err, offset, i;
+    u32 loc, byteloc, bitloc;
+    int ret;
+
+    ecc->sectors = 1 << sector;
+    ret = mtk_ecc_wait_done(ecc, ECC_DECODE, polling);
+    if (ret)
+        return ret;
+
+    stats->corrected = 0;
+    stats->failed = 0;
+
+    offset = (sector >> 2);
+    err = readl(ecc->regs + ECC_DECENUM(offset));
+    err = err >> ((sector % 4) * 8);
+    err &= ERR_MASK;
+    if (err == ERR_MASK) {
+        /* uncorrectable errors */
+        stats->failed++;
+        return 0;
+    }
+
+    stats->corrected += err;
+    stats->bitflips = MAX(stats->bitflips, err);
+
+    for (i = 0; i < err; i++) {
+        loc = readl(ecc->regs + ECC_DECEL(i >> 1));
+        loc >>= ((i & 0x1) << 4);
+        loc &= DECEL_MASK;
+        byteloc = loc >> 3;
+        bitloc = loc & 0x7;
+        data[byteloc] ^= (1 << bitloc);
+    }
+
+    return 0;
+}
+
+void mtk_ecc_adjust_strength(u32 *p)
+{
+    u32 ecc[] = {4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36,
+                 40, 44, 48, 52, 56, 60, 68, 72, 80
+                };
+    u32 i;
+
+    for (i = 0; i < sizeof(ecc) / sizeof(u32); i++) {
+        if (*p <= ecc[i]) {
+            if (!i)
+                *p = ecc[i];
+            else if (*p != ecc[i])
+                *p = ecc[i - 1];
+            return;
+        }
+    }
+
+    *p = ecc[sizeof(ecc) / sizeof(u32) - 1];
+
+    return;
+}
+
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/nand/mtk_nand_bbt.c b/src/bsp/lk/platform/mediatek/mt2712/drivers/nand/mtk_nand_bbt.c
new file mode 100644
index 0000000..91a89e8
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/nand/mtk_nand_bbt.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2017 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <errno.h>
+#include <malloc.h>
+#include <platform/nand/mtk_nand_nal.h>
+#include <platform/nand/mtk_nand_bbt.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define BBT_BLOCK_GOOD          0x03
+#define BBT_BLOCK_WORN          0x02
+#define BBT_BLOCK_RESERVED      0x01
+#define BBT_BLOCK_FACTORY_BAD   0x00
+
+#define BBT_ENTRY_MASK          0x03
+#define BBT_ENTRY_SHIFT         2
+
+#define BBT_PATTERN_LEN         4
+#define BBT_VERSION_LEN         1
+
+static int check_pattern(u8 *buf)
+{
+    uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
+    uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+    if (!memcmp(buf, bbt_pattern, BBT_PATTERN_LEN))
+        return 0;
+    if (!memcmp(buf, mirror_pattern, BBT_PATTERN_LEN))
+        return 0;
+
+    return 1;
+}
+
+static void bbt_mark_entry(struct mtk_nand_chip *chip, int block, u8 mask)
+{
+    u32 offset = BBT_PATTERN_LEN + BBT_VERSION_LEN;
+
+    mask = (mask & BBT_ENTRY_MASK) << ((block & BBT_ENTRY_MASK) * 2);
+    offset += block >> BBT_ENTRY_SHIFT;
+
+    chip->bbt[offset] &= ~(BBT_ENTRY_MASK << ((block & BBT_ENTRY_MASK) * 2));
+    chip->bbt[offset] |= mask;
+}
+
+static u8 bbt_get_entry(struct mtk_nand_chip *chip, int block)
+{
+    u32 offset = BBT_PATTERN_LEN + BBT_VERSION_LEN;
+    u8 entry;
+
+    offset += block >> BBT_ENTRY_SHIFT;
+    entry = chip->bbt[offset];
+    entry >>= (block & BBT_ENTRY_MASK) * 2;
+
+    return entry & BBT_ENTRY_MASK;
+}
+
+static int search_bbt(struct mtk_nand_chip *chip, int bbt_len)
+{
+    int block, i;
+    struct mtk_nand_ops ops;
+
+    memset(&ops, 0, sizeof(ops));
+
+    block = chip->totalsize / chip->blocksize;
+    block -= 1;
+
+    for (i = 0; i < SCAN_BBT_MAXBLOCKS; i++, block--) {
+        ops.mode = NAND_OPS_ECC_DMA_POLL;
+        ops.offset = (u64)block * chip->blocksize;
+        ops.len = (u64)bbt_len;
+        ops.readbuf = chip->bbt;
+        mtk_nand_read(chip, &ops);
+        if (!check_pattern(chip->bbt)) {
+            chip->bbt_block = block;
+            dprintf(CRITICAL, "found bbt at block %d, version 0x%02X\n",
+                    block, chip->bbt[BBT_PATTERN_LEN]);
+            return 0;
+        }
+    }
+
+    dprintf(CRITICAL, "failed to find bbt!\n");
+    return -1;
+}
+
+static int create_bbt(struct mtk_nand_chip *chip, int bbt_len)
+{
+    int i, blocks = chip->totalsize / chip->blocksize;
+
+    memset(chip->bbt, 0xff, bbt_len);
+
+    for (i = 0; i < blocks; i++) {
+        if (mtk_nand_block_checkbad(chip, i * chip->page_per_block)) {
+            bbt_mark_entry(chip, i, BBT_BLOCK_FACTORY_BAD);
+            dprintf(CRITICAL, "block %d is bad\n", i);
+        }
+    }
+
+    dprintf(ALWAYS, "create bbt done!\n");
+
+    return 0;
+}
+
+int mtk_nand_scan_bbt(struct mtk_nand_chip *chip)
+{
+    int len;
+    int ret = 0;
+
+    len = (chip->totalsize / chip->blocksize + 3) >> 2;
+    len += BBT_PATTERN_LEN + BBT_VERSION_LEN;
+    /* allocate bbt memory */
+    chip->bbt = malloc(len);
+    if (!chip->bbt)
+        return -ENOMEM;
+    memset(chip->bbt, 0xff, len);
+    chip->bbt_block = -1;
+
+    if (search_bbt(chip, len)) {
+        ret = create_bbt(chip, len);
+    }
+
+    return ret;
+}
+
+int mtk_nand_isbad_bbt(struct mtk_nand_chip *chip, u32 page)
+{
+    int block = page / chip->page_per_block;
+    u8 mask;
+
+    mask = bbt_get_entry(chip, block);
+
+    return mask != BBT_BLOCK_GOOD;
+}
+
+int mtk_nand_markbad_bbt(struct mtk_nand_chip *chip, u32 page)
+{
+    struct mtk_nand_ops ops;
+    int len, write_len;
+    int ret = 0, block = page / chip->page_per_block;
+
+    bbt_mark_entry(chip, block, BBT_BLOCK_WORN);
+
+    if (chip->bbt_block > 0) {
+        memset(&ops, 0, sizeof(struct mtk_nand_ops));
+        ops.mode = NAND_OPS_ERASE_POLL;
+        ops.offset = (u64)chip->bbt_block * chip->blocksize;
+        ops.len = chip->blocksize;
+        ret = mtk_nand_erase(chip, &ops);
+        if (ret)
+            goto err;
+
+        /* increase bbt version */
+        chip->bbt[BBT_PATTERN_LEN]++;
+
+        len = (chip->totalsize / chip->blocksize + 3) >> 2;
+        len += BBT_PATTERN_LEN + BBT_VERSION_LEN;
+        ops.mode = NAND_OPS_ECC_DMA_POLL;
+        ops.len = chip->pagesize;
+        ops.writebuf = chip->databuf;
+        while (len) {
+            write_len = MIN((u32)len, chip->pagesize);
+            memset(chip->databuf, 0, chip->pagesize);
+            memcpy(chip->databuf, chip->bbt, write_len);
+            ret = mtk_nand_write(chip, &ops);
+            if (ret)
+                goto err;
+            len -= write_len;
+            ops.offset += chip->pagesize;
+        }
+    }
+
+err:
+    return ret;
+}
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/nand/mtk_nand_device.c b/src/bsp/lk/platform/mediatek/mt2712/drivers/nand/mtk_nand_device.c
new file mode 100644
index 0000000..f0cccd7
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/nand/mtk_nand_device.c
@@ -0,0 +1,33 @@
+/*
+* Copyright (c) 2017 MediaTek Inc.
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files
+* (the "Software"), to deal in the Software without restriction,
+* including without limitation the rights to use, copy, modify, merge,
+* publish, distribute, sublicense, and/or sell copies of the Software,
+* and to permit persons to whom the Software is furnished to do so,
+* subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#include <platform/nand/mtk_nand_common.h>
+#include <platform/nand/mtk_nand_nal.h>
+
+#define NAND_OPTIONS_NONE   (0)
+
+struct mtk_nand_flash_dev nand_flash_devs[] = {
+    {"F59D4G81A-45TG-18V", {0xc8, 0xac, 0x90, 0x15, 0x54, 0, 0, 0}, 5, KB(512), KB(128), 2048, 64, 1, 1, 0x10804011, 1024, 4, NAND_OPTIONS_NONE, NAND_OPTIONS_NONE},
+    {"MT29F16G08ADBCA", {0x2c, 0xa5, 0xd1, 0x26, 0x68, 0, 0, 0}, 5, KB(2048), KB(256), 4096, 224, 1, 1, 0x10404011, 1024, 24, NAND_OPTIONS_NONE, NAND_CACHEREAD | NAND_CACHEPRG},
+    {NULL}
+};
+
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/nand/mtk_nand_nal.c b/src/bsp/lk/platform/mediatek/mt2712/drivers/nand/mtk_nand_nal.c
new file mode 100644
index 0000000..bff973c
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/nand/mtk_nand_nal.c
@@ -0,0 +1,875 @@
+/*
+ * Copyright (c) 2017 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <debug.h>
+#include <errno.h>
+#include <kernel/mutex.h>
+#include <malloc.h>
+#include <platform/nand/mtk_nand_bbt.h>
+#include <platform/nand/mtk_nand_common.h>
+#include <platform/nand/mtk_nand_nal.h>
+#include <platform/nand/mtk_nfi_hal.h>
+#include <stdlib.h>
+#include <string.h>
+
+static struct mtk_nand_chip *nandchip;
+
+static int mtk_nand_do_read_ops(struct mtk_nand_chip *chip,
+                                struct mtk_nand_ops *ops);
+static int mtk_nand_do_erase_ops(struct mtk_nand_chip *chip,
+                                 struct mtk_nand_ops *ops);
+static int mtk_nand_do_write_ops(struct mtk_nand_chip *chip,
+                                 struct mtk_nand_ops *ops);
+
+static int mtk_nand_get_controller(struct mtk_nand_chip *chip)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+
+    mutex_acquire(&nfc->lock);
+
+    return 0;
+}
+
+static int mtk_nand_release_controller(struct mtk_nand_chip *chip)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+
+    mutex_release(&nfc->lock);
+
+    return 0;
+}
+
+static int mtk_nand_wait_func(struct mtk_nand_chip *chip, int polling)
+{
+    int status;
+    unsigned long timeo = 1000000;
+
+    chip->cmdfunc(chip, NAND_CMD_STATUS, -1, -1);
+
+    if (!polling) {
+        if (chip->wait_busy_irq(chip))
+            dprintf(CRITICAL, "nand dev ready timeout\n");
+    } else {
+        if (!check_with_timeout(chip->dev_ready(chip), timeo))
+            dprintf(CRITICAL, "nand dev ready timeout\n");
+    }
+
+    status = (int)chip->read_byte(chip);
+
+    return status;
+}
+
+void mtk_nand_wait_ready(struct mtk_nand_chip *chip)
+{
+    unsigned long timeo = 1000000;
+
+    if (!check_with_timeout(chip->dev_ready(chip), timeo))
+        dprintf(CRITICAL, "nand dev ready timeout\n");
+
+    return;
+}
+
+static int mtk_nand_check_wp(struct mtk_nand_chip *chip)
+{
+    /* Check the WP bit */
+    chip->cmdfunc(chip, NAND_CMD_STATUS, -1, -1);
+    return (chip->read_byte(chip) & NAND_STATUS_WP) ? 0 : 1;
+}
+
+static int mtk_nand_block_bad(struct mtk_nand_chip *chip, u64 ofs)
+{
+    int page, res = 0, i = 0;
+    u16 bad;
+
+    if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
+        ofs += chip->blocksize - chip->pagesize;
+
+    page = (int)(ofs / chip->pagesize) % chip->page_per_chip;
+
+    do {
+        chip->cmdfunc(chip, NAND_CMD_READOOB, chip->badblockpos, page);
+        bad = chip->read_byte(chip);
+
+        if (chip->badblockbits == 8)
+            res = bad != 0xFF;
+
+        ofs += chip->pagesize;
+        page = (int)(ofs / chip->pagesize) % chip->page_per_chip;
+        i++;
+    } while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE));
+
+    return res;
+}
+
+int mtk_nand_block_checkbad(struct mtk_nand_chip *chip, u32 page)
+{
+    struct mtk_nand_ops ops;
+    int ret;
+
+    /* block align */
+    page = page / chip->page_per_block * chip->page_per_block;
+
+    if (!(chip->options & NAND_NEED_SCRAMBLING)) {
+        ret = chip->block_bad(chip, (u64)page * chip->pagesize);
+    } else {
+        /*
+         * The output data is randomized if randomizer is on,
+         * we will get a wrong result if just read one byte data.
+         * So, should read page directly.
+         */
+        memset(&ops, 0, sizeof(ops));
+        ops.mode = NAND_OPS_ECC_DMA_POLL;
+        ops.offset = (u64)page * chip->pagesize;
+        ops.len = chip->pagesize;
+        ops.readbuf = chip->databuf;
+        ret = mtk_nand_do_read_ops(chip, &ops);
+        if (ret < 0)
+            ret = 1;
+        else
+            ret = chip->oob_poi[chip->badblockpos] != 0xFF;
+    }
+
+    return ret;
+}
+
+static int mtk_nand_block_isbad_lowlevel(struct mtk_nand_chip *chip, u32 page)
+{
+    if (chip->bbt)
+        return mtk_nand_isbad_bbt(chip, page);
+    else
+        return mtk_nand_block_checkbad(chip, page);
+}
+
+int mtk_nand_block_isbad(struct mtk_nand_chip *chip, u32 page)
+{
+    int ret;
+
+    mtk_nand_get_controller(chip);
+    ret = mtk_nand_block_isbad_lowlevel(chip, page);
+    mtk_nand_release_controller(chip);
+
+    return ret;
+}
+
+int mtk_nand_block_markbad(struct mtk_nand_chip *chip, u32 page)
+{
+    struct mtk_nand_ops ops;
+    u32 i = 0;
+    int ret = 0;
+
+    mtk_nand_get_controller(chip);
+    /* block align */
+    page = page / chip->page_per_block * chip->page_per_block;
+
+    ops.mode = NAND_OPS_ERASE_POLL;
+    ops.offset = (u64)page * chip->pagesize;
+    ops.len = chip->blocksize;
+    mtk_nand_do_erase_ops(chip, &ops);
+
+    if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
+        ops.offset += chip->blocksize - chip->pagesize;
+    ops.mode = NAND_OPS_ECC_DMA_POLL;
+    ops.writebuf = chip->databuf;
+    ops.len = chip->pagesize;
+    ops.oobeccbuf = &chip->oob_poi[chip->oob_free_ecc_size
+                                   + chip->oob_free_raw_size];
+    ops.oobecclen = chip->ecc_steps * chip->fdm_ecc_size;
+    ops.oobeccoffs = 0;
+    ops.oobrawbuf = NULL;
+    memset(chip->databuf, 0, chip->pagesize);
+    memset(ops.oobeccbuf, 0, ops.oobecclen);
+    do {
+        ops.offset += chip->pagesize;
+        mtk_nand_do_write_ops(chip, &ops);
+        i++;
+    } while (i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE));
+
+    mtk_nand_release_controller(chip);
+
+    if (chip->bbt)
+        ret = mtk_nand_markbad_bbt(chip, page);
+
+    return ret;
+}
+
+int nand_reset(struct mtk_nand_chip *chip, int chipnr)
+{
+    /* power on sequence delay */
+    spin(300);
+
+    /*
+     * The CS line has to be released before we can apply the new NAND
+     * interface settings, hence this weird ->select_chip() dance.
+     */
+    chip->select_chip(chip, chipnr);
+    chip->cmdfunc(chip, NAND_CMD_RESET, -1, -1);
+    chip->select_chip(chip, -1);
+
+    return 0;
+}
+
+static inline int mtk_nand_opcode_8bits(unsigned int command)
+{
+    switch (command) {
+        case NAND_CMD_READID:
+        case NAND_CMD_PARAM:
+        case NAND_CMD_GET_FEATURES:
+        case NAND_CMD_SET_FEATURES:
+            return 1;
+        default:
+            break;
+    }
+    return 0;
+}
+
+static void mtk_nand_command_lp(struct mtk_nand_chip *chip, unsigned int command,
+                                int column, int page_addr)
+{
+    /* Emulate NAND_CMD_READOOB */
+    if (command == NAND_CMD_READOOB) {
+        column += chip->pagesize;
+        command = NAND_CMD_READ0;
+    }
+
+    /* Command latch cycle */
+    chip->cmd_ctrl(chip, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+
+    if (column != -1 || page_addr != -1) {
+        int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
+
+        /* Serially input address */
+        if (column != -1) {
+            chip->cmd_ctrl(chip, column, ctrl);
+            ctrl &= ~NAND_CTRL_CHANGE;
+
+            /* Only output a single addr cycle for 8bits opcodes. */
+            if (!mtk_nand_opcode_8bits(command))
+                chip->cmd_ctrl(chip, column >> 8, ctrl);
+        }
+        if (page_addr != -1) {
+            chip->cmd_ctrl(chip, page_addr, ctrl);
+            chip->cmd_ctrl(chip, page_addr >> 8, NAND_NCE | NAND_ALE);
+            /* One more address cycle for devices > 128MiB */
+            if (chip->chipsize > (128 << 20))
+                chip->cmd_ctrl(chip, page_addr >> 16, NAND_NCE | NAND_ALE);
+        }
+    }
+    chip->cmd_ctrl(chip, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+
+    /*
+     * Program and erase have their own busy handlers status, sequential
+     * in and status need no delay.
+     */
+    switch (command) {
+        case NAND_CMD_CACHEDPROG:
+        case NAND_CMD_PAGEPROG:
+        case NAND_CMD_ERASE1:
+        case NAND_CMD_ERASE2:
+        case NAND_CMD_SEQIN:
+        case NAND_CMD_STATUS:
+            return;
+
+        case NAND_CMD_RNDOUT:
+            /* No ready / busy check necessary */
+            chip->cmd_ctrl(chip, NAND_CMD_RNDOUTSTART, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+            chip->cmd_ctrl(chip, NAND_CMD_NONE,
+                           NAND_NCE | NAND_CTRL_CHANGE);
+            return;
+
+        case NAND_CMD_READ0:
+            chip->cmd_ctrl(chip, NAND_CMD_READSTART, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+            chip->cmd_ctrl(chip, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+
+        /* This applies to read commands */
+        default:
+            break;
+    }
+
+    mtk_nand_wait_ready(chip);
+
+    return;
+}
+
+static void mtk_nand_set_defaults(struct mtk_nand_chip *chip)
+{
+    /* chip_delay setup set 20us if not */
+    chip->chip_delay = 20;
+
+    /* command function*/
+    chip->cmdfunc = mtk_nand_command_lp;
+
+    /* wait function */
+    chip->waitfunc = mtk_nand_wait_func;
+
+    /* bad block check */
+    chip->block_bad = mtk_nand_block_bad;
+
+    /* variable defalut value */
+    chip->badblockbits = 8;
+    chip->badblockpos = 0;
+
+    chip->activechip = -1;
+
+    return;
+}
+
+int mtk_nand_flash_get(struct mtk_nand_chip *chip, int maxchips)
+{
+    int i;
+    u8 id_data[8];
+    struct mtk_nand_flash_dev *type = nand_flash_devs;
+
+    nand_reset(chip, 0);
+
+    /* Select the device */
+    chip->select_chip(chip, 0);
+
+    /* Send the command for reading device ID */
+    chip->cmdfunc(chip, NAND_CMD_READID, 0x00, -1);
+
+    /* Read entire ID string */
+    for (i = 0; i < 8; i++) {
+        id_data[i] = chip->read_byte(chip);
+        LTRACEF("nand id[%d] [%x] \n", i, id_data[i]);
+    }
+
+    for (; type->name != NULL; type++) {
+        if (!strncmp((char const*)type->id, (char const*)id_data, type->id_len)) {
+            dprintf(ALWAYS, "nand found [%s] \n", type->name);
+            break;
+        }
+    }
+
+    chip->select_chip(chip, -1);
+    if (!type->name) {
+        return -ENODEV;
+    }
+
+    chip->numchips = 1;
+
+    /* Check for a chip array */
+    for (i = 1; i < maxchips; i++) {
+        /* See comment in nand_get_flash_type for reset */
+        nand_reset(chip, i);
+
+        chip->select_chip(chip, i);
+        /* Send the command for reading device ID */
+        chip->cmdfunc(chip, NAND_CMD_READID, 0x00, -1);
+        /* Read manufacturer and device IDs */
+        if (id_data[0] != chip->read_byte(chip) ||
+                id_data[1] != chip->read_byte(chip)) {
+            chip->select_chip(chip, -1);
+            break;
+        }
+        dprintf(ALWAYS, "chip %d is found\n", i);
+        chip->select_chip(chip, -1);
+        chip->numchips++;
+    }
+
+    /* set nand chip parameters */
+    chip->pagesize = type->pagesize;
+    chip->oobsize = type->oobsize;
+    chip->bits_per_cell = type->bits_per_cell;
+    /* KB to B */
+    chip->chipsize = ((u64)type->chipsize) << 10;
+    chip->blocksize = type->erasesize;
+    chip->bbt_options |= type->bbt_options;
+    chip->options |= type->options;
+    chip->ecc_size = type->ecc_size;
+    chip->ecc_strength = type->ecc_strength;
+    chip->fdm_ecc_size = type->fdmeccsize;
+
+    chip->totalsize = i * chip->chipsize;
+
+    chip->ecc_steps = chip->pagesize / chip->ecc_size;
+    if (nand_is_slc(chip)) {
+        if (chip->ecc_steps == 2)
+            chip->subpagesize = chip->pagesize / 2;
+        else if (chip->ecc_steps > 2)
+            chip->subpagesize = chip->pagesize / 4;
+        else
+            chip->subpagesize = chip->pagesize;
+    }
+    chip->page_per_block = chip->blocksize / chip->pagesize;
+    chip->page_per_chip = chip->chipsize / chip->pagesize;
+
+    chip->databuf = (u8 *)memalign(16, chip->pagesize + chip->oobsize);
+    if (!chip->databuf)
+        return -ENOMEM;
+    chip->oob_poi = chip->databuf + chip->pagesize;
+
+    return 0;
+}
+
+int mtk_nand_scan(struct mtk_nand_chip *chip, int maxchips)
+{
+    int ret;
+
+    /* Set the defaults */
+    mtk_nand_set_defaults(chip);
+
+    ret = mtk_nand_flash_get(chip, maxchips);
+    if (ret) {
+        dprintf(CRITICAL, "no nand device found\n");
+        return ret;
+    }
+
+    return 0;
+}
+
+int mtk_nand_scan_tail(struct mtk_nand_chip *chip)
+{
+    int ret;
+
+    /* scan bad block table */
+    ret = mtk_nand_scan_bbt(chip);
+
+    return ret;
+}
+
+static int mtk_nand_transfer_oob(struct mtk_nand_chip *chip, struct mtk_nand_ops *ops)
+{
+    int ret = 0;
+    u32 parity_size;
+
+    parity_size = chip->oobsize - chip->oob_free_raw_size
+                  - chip->oob_free_ecc_size;
+
+    if (ops->oobeccbuf && chip->transfer_oob_ecc) {
+        if (ops->oobeccoffs >= chip->oob_free_ecc_size
+            || ops->oobecclen > chip->oob_free_ecc_size - ops->oobeccoffs)
+            return -EINVAL;
+        ret = chip->transfer_oob_ecc(chip, ops->oobeccbuf, ops->oobeccoffs,
+                                     ops->oobecclen);
+        if (ret)
+            return ret;
+    }
+
+    if (ops->oobrawbuf && chip->transfer_oob_raw) {
+        if (ops->oobrawoffs >= chip->oob_free_raw_size
+            || ops->oobrawlen > chip->oob_free_raw_size - ops->oobrawoffs)
+            return -EINVAL;
+        ret = chip->transfer_oob_raw(chip, ops->oobrawbuf, ops->oobrawoffs,
+                                     ops->oobrawlen);
+    }
+
+    if (ops->oobparitybuf && chip->transfer_oob_parity) {
+        if (ops->oobparityoffs >= parity_size
+            || ops->oobparitylen > parity_size - ops->oobparityoffs)
+            return -EINVAL;
+        ret = chip->transfer_oob_parity(chip, ops->oobparitybuf, ops->oobparityoffs,
+                                        ops->oobparitylen);
+    }
+
+    return ret;
+}
+
+static int mtk_nand_do_read_ops(struct mtk_nand_chip *chip, struct mtk_nand_ops *ops)
+{
+    int chipnr, page, realpage, col, aligned;
+    u8 *buf, *bufpoi;
+    u64 readlen = ops->len, from = ops->offset;
+    u32 bytes, ecc_failures = chip->stats.failed;
+    int ret = 0, ecc_fail = 0, max_bitflips = 0;
+    bool enable_cache = false;
+
+    chipnr = (int)(from / chip->chipsize);
+    chip->select_chip(chip, chipnr);
+
+    realpage = (int)(from / chip->pagesize);
+    page = realpage % chip->page_per_chip;
+    if (readlen == 0)
+        return 0;
+    if (NAND_HAS_CACHEREAD(chip) && (int)((from + readlen - 1) / chip->pagesize) > realpage)
+        enable_cache = true;
+
+    col = (int)(from & (chip->pagesize - 1));
+
+    buf = ops->readbuf;
+
+    while (1) {
+        bytes = MIN(chip->pagesize - col, readlen);
+        aligned = (bytes == chip->pagesize);
+        bufpoi = aligned ? buf : chip->databuf;
+
+        memset(chip->oob_poi, 0xff, chip->oobsize);
+
+        /* send read page command */
+        LTRACEF("[nand] read page %d chip %d\n", page, chipnr);
+        chip->enable_randomizer(chip, page, RAND_DECODE, 0);
+
+        if (!enable_cache || realpage == (int)(from / chip->pagesize))
+            chip->cmdfunc(chip, NAND_CMD_READ0, 0x00, page);
+
+        if (enable_cache) {
+            if ((readlen - bytes) == 0)
+                chip->cmdfunc(chip, NAND_CMD_READCACHELAST, -1, -1);
+            else
+                chip->cmdfunc(chip, NAND_CMD_READCACHESEQ, -1, -1);
+        }
+
+        if (!aligned) {
+            if (ops->mode == NAND_OPS_ECC_DMA_IRQ)
+                ret = chip->read_subpage_ecc_dma_irq(chip, col, bytes, bufpoi, page);
+            else if (ops->mode == NAND_OPS_ECC_DMA_POLL)
+                ret = chip->read_subpage_ecc_dma_polling(chip, col, bytes, bufpoi, page);
+            else if (ops->mode == NAND_OPS_ECC_PIO_IRQ)
+                ret = chip->read_subpage_ecc_pio_irq(chip, col, bytes, bufpoi, page);
+            else if (ops->mode == NAND_OPS_ECC_PIO_POLL)
+                ret = chip->read_subpage_ecc_pio_polling(chip, col, bytes, bufpoi, page);
+        } else {
+            if (ops->mode == NAND_OPS_RAW_DMA_IRQ)
+                ret = chip->read_page_raw_dma_irq(chip, bufpoi, page);
+            else if (ops->mode == NAND_OPS_RAW_DMA_POLL)
+                ret = chip->read_page_raw_dma_polling(chip, bufpoi, page);
+            else if (ops->mode == NAND_OPS_RAW_PIO_IRQ)
+                ret = chip->read_page_raw_pio_irq(chip, bufpoi, page);
+            else if (ops->mode == NAND_OPS_RAW_PIO_POLL)
+                ret = chip->read_page_raw_pio_polling(chip, bufpoi, page);
+            else if (ops->mode == NAND_OPS_ECC_DMA_IRQ)
+                ret = chip->read_page_ecc_dma_irq(chip, bufpoi, page);
+            else if (ops->mode == NAND_OPS_ECC_DMA_POLL)
+                ret = chip->read_page_ecc_dma_polling(chip, bufpoi, page);
+            else if (ops->mode == NAND_OPS_ECC_PIO_IRQ)
+                ret = chip->read_page_ecc_pio_irq(chip, bufpoi, page);
+            else if (ops->mode == NAND_OPS_ECC_PIO_POLL)
+                ret = chip->read_page_ecc_pio_polling(chip, bufpoi, page);
+        }
+        chip->disable_randomizer(chip);
+        if (ret < 0)
+            break;
+
+        max_bitflips = MAX(max_bitflips, ret);
+
+        ret = mtk_nand_transfer_oob(chip, ops);
+        if (ret) {
+            max_bitflips = ret;
+            break;
+        }
+
+        if (chip->stats.failed - ecc_failures) {
+            ecc_fail = 1;
+            break;
+        }
+
+        if (!aligned)
+            memcpy(buf, chip->databuf + col, bytes);
+
+        readlen -= bytes;
+        buf += bytes;
+
+        if (!readlen)
+            break;
+
+        /* For subsequent reads align to page boundary */
+        col = 0;
+        /* Increment page address */
+        realpage++;
+
+        page = realpage % chip->page_per_chip;
+        /* Check, if we cross a chip boundary */
+        if (!page) {
+            chipnr++;
+            chip->select_chip(chip, -1);
+            chip->select_chip(chip, chipnr);
+        }
+    }
+    chip->select_chip(chip, -1);
+
+    if (ecc_fail)
+        return -EBADMSG;
+
+    return max_bitflips;
+}
+
+int mtk_nand_read(struct mtk_nand_chip *chip, struct mtk_nand_ops *ops)
+{
+    int ret;
+
+    mtk_nand_get_controller(chip);
+    ret = mtk_nand_do_read_ops(chip, ops);
+    mtk_nand_release_controller(chip);
+
+    return ret;
+}
+
+static int mtk_nand_fill_oob(struct mtk_nand_chip *chip, struct mtk_nand_ops *ops)
+{
+    int ret = 0;
+    u32 parity_size;
+
+    parity_size = chip->oobsize - chip->oob_free_raw_size
+                  - chip->oob_free_ecc_size;
+
+    memset(chip->oob_poi, 0xff, chip->oobsize);
+
+    if (ops->oobeccbuf && chip->fill_oob_ecc) {
+        if (ops->oobeccoffs >= chip->oob_free_ecc_size
+            || ops->oobecclen > chip->oob_free_ecc_size - ops->oobeccoffs)
+            return -EINVAL;
+        ret = chip->fill_oob_ecc(chip, ops->oobeccbuf, ops->oobeccoffs,
+                                 ops->oobecclen);
+        if (ret)
+            return ret;
+    }
+
+    if (ops->oobrawbuf && chip->fill_oob_raw) {
+        if (ops->oobrawoffs >= chip->oob_free_raw_size
+            || ops->oobrawlen > chip->oob_free_raw_size - ops->oobrawoffs)
+            return -EINVAL;
+        ret = chip->fill_oob_raw(chip, ops->oobrawbuf, ops->oobrawoffs,
+                                 ops->oobrawlen);
+    }
+
+    if (ops->oobparitybuf && chip->fill_oob_parity) {
+        if (ops->oobparityoffs >= parity_size
+            || ops->oobparitylen > parity_size - ops->oobparityoffs)
+            return -EINVAL;
+        ret = chip->fill_oob_parity(chip, ops->oobparitybuf, ops->oobparityoffs,
+                                    ops->oobparitylen);
+    }
+
+    return ret;
+}
+
+static int mtk_nand_do_write_ops(struct mtk_nand_chip *chip, struct mtk_nand_ops *ops)
+{
+    int chipnr, realpage, page, col, aligned;
+    u32 bytes, writelen = ops->len;
+    u64 to = ops->offset;
+    const u8 *buf = ops->writebuf;
+    const u8 *bufpoi;
+    int ret = 0, status, polling_wait = 1;
+    bool enable_cache = false;
+
+    /* Reject writes, which are not subpage aligned */
+    if (!IS_ALIGNED(to, chip->subpagesize) || !IS_ALIGNED(ops->len, chip->subpagesize)) {
+        dprintf(CRITICAL, "attempt to write non page aligned data (offset 0x%llx, len 0x%llx)\n", to, ops->len);
+        return -EINVAL;
+    }
+
+    col = to & (chip->pagesize - 1);
+    chipnr = (int)(to / chip->chipsize);
+    chip->select_chip(chip, chipnr);
+
+    /* Check, if it is write protected */
+    if (mtk_nand_check_wp(chip)) {
+        ret = -EIO;
+        dprintf(CRITICAL, "write protected!\n");
+        goto err_out;
+    }
+
+    realpage = (int)(to / chip->pagesize);
+    page = realpage % chip->page_per_chip;
+    if (NAND_HAS_CACHEPROG(chip) && (int)((to + writelen) / chip->pagesize) > realpage)
+        enable_cache = true;
+
+    while (1) {
+        bytes = MIN(chip->pagesize - col, writelen);
+        aligned = (bytes == chip->pagesize);
+        bufpoi = aligned ? buf : chip->databuf;
+
+        if (!aligned) {
+            memset(chip->databuf, 0xff, chip->pagesize);
+            memcpy(chip->databuf + col, buf, bytes);
+        }
+
+        ret = mtk_nand_fill_oob(chip, ops);
+        if (ret)
+            break;
+
+        LTRACEF("[nand] write page %d chip %d\n", page, chipnr);
+        chip->enable_randomizer(chip, page, RAND_ENCODE, 0);
+        chip->cmdfunc(chip, NAND_CMD_SEQIN, 0x00, page);
+
+        if (!aligned) {
+            if (ops->mode == NAND_OPS_ECC_DMA_IRQ) {
+                polling_wait = 0;
+                ret = chip->write_subpage_ecc_dma_irq(chip, col, bytes, bufpoi, page);
+            }
+            else if (ops->mode == NAND_OPS_ECC_DMA_POLL)
+                ret = chip->write_subpage_ecc_dma_polling(chip, col, bytes, bufpoi, page);
+            else if (ops->mode == NAND_OPS_ECC_PIO_IRQ) {
+                polling_wait = 0;
+                ret = chip->write_subpage_ecc_pio_irq(chip, col, bytes, bufpoi, page);
+            }
+            else if (ops->mode == NAND_OPS_ECC_PIO_POLL)
+                ret = chip->write_subpage_ecc_pio_polling(chip, col, bytes, bufpoi, page);
+        }
+        else {
+            if (ops->mode == NAND_OPS_RAW_DMA_IRQ) {
+                polling_wait = 0;
+                ret = chip->write_page_raw_dma_irq(chip, bufpoi, page);
+            }
+            else if (ops->mode == NAND_OPS_RAW_DMA_POLL)
+                ret = chip->write_page_raw_dma_polling(chip, bufpoi, page);
+            else if (ops->mode == NAND_OPS_RAW_PIO_IRQ) {
+                polling_wait = 0;
+                ret = chip->write_page_raw_pio_irq(chip, bufpoi, page);
+            }
+            else if (ops->mode == NAND_OPS_RAW_PIO_POLL)
+                ret = chip->write_page_raw_pio_polling(chip, bufpoi, page);
+            else if (ops->mode == NAND_OPS_ECC_DMA_IRQ) {
+                polling_wait = 0;
+                ret = chip->write_page_ecc_dma_irq(chip, bufpoi, page);
+            }
+            else if (ops->mode == NAND_OPS_ECC_DMA_POLL)
+                ret = chip->write_page_ecc_dma_polling(chip, bufpoi, page);
+            else if (ops->mode == NAND_OPS_ECC_PIO_IRQ) {
+                polling_wait = 0;
+                ret = chip->write_page_ecc_pio_irq(chip, bufpoi, page);
+            }
+            else if (ops->mode == NAND_OPS_ECC_PIO_POLL)
+                ret = chip->write_page_ecc_pio_polling(chip, bufpoi, page);
+        }
+        chip->disable_randomizer(chip);
+        if (ret < 0)
+            break;
+
+        if (!enable_cache || (writelen - bytes) == 0)
+            chip->cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
+        else
+            chip->cmdfunc(chip, NAND_CMD_CACHEDPROG, -1, -1);
+        status = chip->waitfunc(chip, polling_wait);
+        if (status & NAND_STATUS_FAIL) {
+            ret = -EIO;
+            dprintf(CRITICAL, "write failed at page 0x%x\n", realpage);
+            goto err_out;
+        }
+
+        writelen -= bytes;
+        if (!writelen)
+            break;
+
+        col = 0;
+        buf += bytes;
+        realpage++;
+
+        page = realpage % chip->page_per_chip;
+        /* Check, if we cross a chip boundary */
+        if (!page) {
+            chipnr++;
+            chip->select_chip(chip, -1);
+            chip->select_chip(chip, chipnr);
+        }
+    }
+
+err_out:
+    chip->select_chip(chip, -1);
+
+    return ret;
+}
+
+int mtk_nand_write(struct mtk_nand_chip *chip, struct mtk_nand_ops *ops)
+{
+    int ret;
+
+    mtk_nand_get_controller(chip);
+    ret = mtk_nand_do_write_ops(chip, ops);
+    mtk_nand_release_controller(chip);
+
+    return ret;
+}
+
+static int mtk_nand_do_erase_ops(struct mtk_nand_chip *chip, struct mtk_nand_ops *ops)
+{
+    u64 offset = ops->offset;
+    u64 eraselen = ops->len;
+    int page, status, ret = 0, chipnr, polling_wait = 0;
+
+    if ((offset % chip->blocksize) || (eraselen % chip->blocksize)) {
+        dprintf(CRITICAL, "erase is not aligned (off 0x%llx, len 0x%llx)\n", offset, eraselen);
+        return -EINVAL;
+    }
+
+    page = (int)(offset / chip->pagesize);
+    chipnr = (int)(offset / chip->chipsize);
+
+    chip->select_chip(chip, chipnr);
+
+    /* Check, if it is write protected */
+    if (mtk_nand_check_wp(chip)) {
+        ret = -EIO;
+        dprintf(CRITICAL, "write protected!\n");
+        goto err_out;
+    }
+
+    while (1) {
+        if (mtk_nand_block_isbad_lowlevel(chip, page)) {
+            ret = -EIO;
+            dprintf(CRITICAL, "attempt to erase bad block at page 0x%x\n",
+                    page);
+            goto err_out;
+        }
+
+        LTRACEF("[nand] erase page %d chip %d\n", page, chipnr);
+        chip->cmdfunc(chip, NAND_CMD_ERASE1, -1, (page % chip->page_per_chip));
+        chip->cmdfunc(chip, NAND_CMD_ERASE2, -1, -1);
+        if (ops->mode == NAND_OPS_ERASE_IRQ)
+            polling_wait = 0;
+        else if (ops->mode == NAND_OPS_ERASE_POLL)
+            polling_wait = 1;
+        status = chip->waitfunc(chip, polling_wait);
+
+        if (status & NAND_STATUS_FAIL) {
+            ret = -EIO;
+            dprintf(CRITICAL, "erase failed at page 0x%x\n", page);
+            goto err_out;
+        }
+
+        eraselen -= chip->blocksize;
+        if (!eraselen)
+            break;
+        page += chip->page_per_block;
+
+        if (eraselen && !(page % chip->page_per_chip)) {
+            chipnr++;
+            chip->select_chip(chip, -1);
+            chip->select_chip(chip, chipnr);
+        }
+    }
+err_out:
+    chip->select_chip(chip, -1);
+
+    return ret;
+}
+
+int mtk_nand_erase(struct mtk_nand_chip *chip, struct mtk_nand_ops *ops)
+{
+    int ret;
+
+    mtk_nand_get_controller(chip);
+    ret = mtk_nand_do_erase_ops(chip, ops);
+    mtk_nand_release_controller(chip);
+
+    return ret;
+}
+
+int mtk_nand_init(void)
+{
+    return mtk_nfc_nand_chip_init(&nandchip);
+}
+
+struct mtk_nand_chip *mtk_get_nand_chip(void)
+{
+    return nandchip;
+}
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/nand/mtk_nand_nftl.c b/src/bsp/lk/platform/mediatek/mt2712/drivers/nand/mtk_nand_nftl.c
new file mode 100644
index 0000000..3abc464
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/nand/mtk_nand_nftl.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <err.h>
+#include <errno.h>
+#include <lib/nftl.h>
+#include <malloc.h>
+#include <platform/mtk_bio_ioctl.h>
+#include <platform/nand/mtk_nand_nal.h>
+#include <pow2.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+struct mtk_nand_chip *chip;
+
+static ssize_t nand_write(struct nftl_info *info, const void *buf, off_t offset,
+                          ssize_t len)
+{
+    struct mtk_nand_ops ops;
+    int ret;
+
+    memset(&ops, 0, sizeof(ops));
+    ops.mode = NAND_OPS_ECC_DMA_POLL;
+    ops.offset = (u64)offset;
+    ops.len = (u64)len;
+    ops.writebuf = buf;
+
+    ret = mtk_nand_write(chip, &ops);
+
+    return (ret < 0) ? ret : len;
+}
+
+static ssize_t nand_read(struct nftl_info *info, void *buf, off_t offset,
+                         ssize_t len)
+{
+    struct mtk_nand_ops ops;
+    int ret;
+
+    memset(&ops, 0, sizeof(ops));
+    ops.mode = NAND_OPS_ECC_DMA_POLL;
+    ops.offset = (u64)offset;
+    ops.len = (u64)len;
+    ops.readbuf = buf;
+
+    ret = mtk_nand_read(chip, &ops);
+
+    return (ret < 0) ? ret : len;
+}
+
+static ssize_t nand_erase(struct nftl_info *info, off_t offset, ssize_t len)
+{
+    struct mtk_nand_ops ops;
+    int ret;
+
+    memset(&ops, 0, sizeof(ops));
+    ops.mode = NAND_OPS_ERASE_POLL;
+    ops.offset = (u64)offset;
+    ops.len = (u64)len;
+
+    ret = (ssize_t)mtk_nand_erase(chip, &ops);
+
+    return (ret < 0) ? ret : len;
+}
+
+static int nand_block_isbad(struct nftl_info *info, u32 page)
+{
+    return mtk_nand_block_isbad(chip, page);
+}
+
+static int nand_ioctl(struct nftl_info *info, int request, void *argp)
+{
+    int ret = NO_ERROR;
+
+    switch (request) {
+        case BIO_IOCTL_QUERY_CAP_REWRITABLE:
+            *(bool *)argp = false;
+            break;
+        default:
+            ret = ERR_INVALID_ARGS;
+            break;
+    }
+
+    return ret;
+}
+
+int nand_init_device(void)
+{
+    struct nftl_info *info;
+    int ret;
+
+    ret = mtk_nand_init();
+    if (ret) {
+        dprintf(CRITICAL, "nand device init error (%d)!\n", ret);
+        return ret;
+    }
+
+    chip = mtk_get_nand_chip();
+
+    info = nftl_add_master("nand0");
+    if (!info)
+        return ERR_NO_MEMORY;
+
+    info->erase_size = chip->blocksize;
+    info->write_size = chip->pagesize;
+    info->total_size = chip->totalsize;
+    info->block_isbad = nand_block_isbad;
+    info->read = nand_read;
+    info->write = nand_write;
+    info->erase = nand_erase;
+    info->ioctl = nand_ioctl;
+
+    ret = nftl_mount_bdev(info);
+
+    return ret;
+}
+
+void nand_dump_device_info(void)
+{
+    dprintf(ALWAYS, "chip size: %#llx B\n", chip->chipsize);
+    dprintf(ALWAYS, "block size: %u B\n", chip->blocksize);
+    dprintf(ALWAYS, "page size: %u B\n", chip->pagesize);
+    dprintf(ALWAYS, "oob size: %u B\n", chip->oobsize);
+    dprintf(ALWAYS, "bits per cell: %d\n", chip->bits_per_cell);
+    dprintf(ALWAYS, "ecc size: %d\n", chip->ecc_size);
+    dprintf(ALWAYS, "ecc strength: %d\n", chip->ecc_strength);
+    dprintf(ALWAYS, "all fdm ecc size: %d\n", chip->oob_free_ecc_size);
+    dprintf(ALWAYS, "all fdm raw size: %d\n", chip->oob_free_raw_size);
+}
+
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/nand/mtk_nand_test.c b/src/bsp/lk/platform/mediatek/mt2712/drivers/nand/mtk_nand_test.c
new file mode 100644
index 0000000..3eefdab
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/nand/mtk_nand_test.c
@@ -0,0 +1,376 @@
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#if WITH_LIB_CONSOLE
+
+#include <debug.h>
+#include <err.h>
+#include <lib/bio.h>
+#include <lib/console.h>
+#include <lib/mempool.h>
+#include <platform.h>
+#include <platform/nand/mtk_nand_nal.h>
+#include <platform/nand/mtk_nand_bbt.h>
+#include <platform/nand/nand.h>
+#include <rand.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define TEST_BUFFER_SIZE (0x80000)
+
+static void gen_rand_data(u8 *buf, size_t len)
+{
+    u32 i;
+
+    for (i = 0; i < len; i++)
+        buf[i] = (u8)rand();
+}
+
+static int compare_data(u8 *src, u8 *dest, size_t len)
+{
+    u32 i;
+
+    for (i = 0; i < len; i++) {
+        if (src[i] != dest[i]) {
+            printf("ERROR at %d: should be 0x%x, is 0x%x\n", i, src[i], dest[i]);
+            return ERR_IO;
+        }
+    }
+
+    return 0;
+}
+
+static int nand_speed_test(const char *device)
+{
+    int ret = 0;
+    u8 *wbuf, *rbuf;
+    bdev_t *dev;
+    ssize_t valid_total_size, left, test_size;
+    lk_bigtime_t delta_time, start_time;
+
+    dev = bio_open(device);
+    if (!dev) {
+        dev = bio_open_by_label(device);
+        if (!dev) {
+            printf("error opening block device: %s\n", device);
+            return ERR_NOT_FOUND;
+        }
+    }
+
+    wbuf = mempool_alloc(TEST_BUFFER_SIZE, MEMPOOL_ANY);
+    if (wbuf == NULL) {
+        ret = ERR_NO_MEMORY;
+        goto closedev;
+    }
+
+    rbuf = mempool_alloc(TEST_BUFFER_SIZE, MEMPOOL_ANY);
+    if (rbuf == NULL) {
+        ret = ERR_NO_MEMORY;
+        goto freewbuf;
+    }
+
+    gen_rand_data(wbuf, TEST_BUFFER_SIZE);
+
+    printf("begin erase test!\n");
+    delta_time = current_time_hires();
+    valid_total_size = bio_erase(dev, 0, dev->total_size);
+    delta_time = current_time_hires() - delta_time;
+    printf("erase speed is: %llu KB/s\n", (valid_total_size * 1000000) / (delta_time * 1024));
+
+    printf("begin write test!\n");
+    left = valid_total_size;
+    delta_time = current_time_hires();
+    while (left) {
+        test_size = MIN(TEST_BUFFER_SIZE, left);
+        ret = bio_write(dev, wbuf, valid_total_size - left, test_size);
+        if (ret < 0)
+            goto freerbuf;
+        left -= test_size;
+    };
+    delta_time = current_time_hires() - delta_time;
+    printf("write speed is: %llu KB/s\n", (valid_total_size * 1000000) / (delta_time * 1024));
+
+    printf("begin read test!\n");
+    left = valid_total_size;
+    delta_time = 0;
+    while (left) {
+        start_time = current_time_hires();
+        test_size = MIN(TEST_BUFFER_SIZE, left);
+        ret = bio_read(dev, rbuf, valid_total_size - left, test_size);
+        if (ret < 0)
+            goto freerbuf;
+        delta_time += current_time_hires() - start_time;
+        ret = compare_data(wbuf, rbuf, test_size);
+        if (ret < 0)
+            goto freerbuf;
+        left -= test_size;
+    }
+    printf("read speed is: %llu KB/s\n", (valid_total_size * 1000000) / (delta_time * 1024));
+
+freerbuf:
+    mempool_free(rbuf);
+freewbuf:
+    mempool_free(wbuf);
+closedev:
+    bio_close(dev);
+
+    return ret;
+}
+
+#define CBIT(v, n) ((v) & (1 << (n)))
+#define BCLR(v, n) ((v) = (v) & ~(1 << (n)))
+static u32 insert_biterrors(u32 byte, u32 boundary, u32 errors, u8 *buf)
+{
+    int bit;
+
+    while (byte < boundary && errors) {
+        for (bit = 7; bit >= 0; bit--) {
+            if (CBIT(buf[byte], bit)) {
+                BCLR(buf[byte], bit);
+                printf("%s: Inserted biterror @ %u/%u\n", __func__, byte, bit);
+                if (--errors == 0)
+                    break;
+            }
+        }
+        byte++;
+    }
+
+    return errors;
+}
+
+static int nand_bit_errors_test(u32 page, u32 error_num)
+{
+    struct mtk_nand_chip *chip = mtk_get_nand_chip();
+    struct mtk_nand_ops ops;
+    u8 *rbuf, *wbuf;
+    u32 buf_size, i, byte, parity_per_sector;
+    u32 parity_bytes = chip->ecc_strength * ECC_PARITY_BITS / 8 - 1;
+    u32 errs;
+    int ret;
+
+    /* error num 0 means full ecc strength */
+    if (error_num == 0)
+        error_num = chip->ecc_strength;
+    errs = error_num;
+
+    buf_size = chip->pagesize + chip->oobsize;
+
+    rbuf = mempool_alloc(buf_size, MEMPOOL_ANY);
+    if (rbuf == NULL)
+        return ERR_NO_MEMORY;
+    memset(rbuf, 0xff, buf_size);
+
+    wbuf = mempool_alloc(buf_size, MEMPOOL_ANY);
+    if (wbuf == NULL) {
+        ret = ERR_NO_MEMORY;
+        goto freerbuf;
+    }
+    memset(wbuf, 0xff, buf_size);
+
+    if (mtk_nand_block_isbad(chip, page)) {
+        printf("%s: page %u is bad!\n", __func__, page);
+        ret = ERR_FAULT;
+        goto freewbuf;
+    }
+
+    memset(&ops, 0, sizeof(ops));
+
+    ops.mode = NAND_OPS_ECC_DMA_POLL;
+    ops.offset = (u64)page * chip->pagesize;
+    ops.len = (u64)chip->pagesize;
+    ops.readbuf = rbuf;
+    ret = mtk_nand_read(chip, &ops);
+    if (ret < 0)
+        goto freewbuf;
+    printf("%s: max bit errors before rewrite: %d\n", __func__, ret);
+
+    memset(rbuf, 0xff, buf_size);
+    ops.mode = NAND_OPS_RAW_DMA_POLL;
+    ops.oobeccbuf = ops.readbuf + chip->pagesize;
+    ops.oobeccoffs = 0;
+    ops.oobecclen = chip->oob_free_ecc_size;
+    ops.oobrawbuf = ops.oobeccbuf + chip->oob_free_ecc_size;
+    ops.oobrawoffs = 0;
+    ops.oobrawlen = chip->oob_free_raw_size;
+    ops.oobparitybuf = ops.oobrawbuf + chip->oob_free_raw_size;
+    ops.oobparityoffs = 0;
+    ops.oobparitylen = chip->oobsize - chip->oob_free_ecc_size
+                       - chip->oob_free_raw_size;
+    parity_per_sector = ops.oobparitylen / chip->ecc_steps;
+    ret = mtk_nand_read(chip, &ops);
+    if (ret < 0)
+        goto freewbuf;
+    memcpy(wbuf, rbuf, chip->pagesize + chip->oobsize);
+
+    /* introduce deliberate bit errors */
+    for (i = 0; i < chip->ecc_steps; i++) {
+        errs = error_num;
+
+        /* one bit error in fdm data area */
+        printf("%s: sector %u insert fdm data area error\n", __func__, i);
+        byte = (u32)rand() % chip->fdm_ecc_size;
+        byte += i * chip->fdm_ecc_size;
+        ret = insert_biterrors(byte, (i + 1) * chip->fdm_ecc_size, 1,
+                               ops.oobeccbuf);
+        errs -= (1 - ret);
+
+        if (errs == 0)
+            continue;
+
+        /* one bit error in parity data area */
+        printf("%s: sector %u insert parity data area error\n", __func__, i);
+        byte = (u32)rand() % parity_bytes;
+        byte += i * parity_per_sector;
+        ret = insert_biterrors(byte, (i + 1) * parity_per_sector, 1,
+                               ops.oobparitybuf);
+
+        errs -= (1 - ret);
+
+        if (errs == 0)
+            continue;
+
+        /* error in main data area */
+        printf("%s: sector %u insert main data area error\n", __func__, i);
+        byte = (u32)rand() % chip->ecc_size;
+        byte += i * chip->ecc_size;
+        ret = insert_biterrors(byte, (i + 1) * chip->ecc_size, errs,
+                               ops.readbuf);
+        errs -= (errs - ret);
+
+        if (errs)
+            printf("%s: sector %d insert errors not enough, left %u\n",
+                   __func__, i, errs);
+    }
+
+    ops.writebuf = rbuf;
+    ret = mtk_nand_write(chip, &ops);
+    if (ret < 0)
+        goto freewbuf;
+
+    /* readback */
+    memset(rbuf, 0xff, chip->pagesize + chip->oobsize);
+    ops.mode = NAND_OPS_ECC_DMA_POLL;
+    ops.oobrawbuf = NULL;
+    ops.oobparitybuf = NULL;
+    ret = mtk_nand_read(chip, &ops);
+    if (ret < 0)
+        goto freewbuf;
+    else
+        printf("%s: max bit errors after rewrite: %d\n", __func__, ret);
+
+    /* verify data */
+    for (i = 0; i < chip->pagesize + chip->oob_free_ecc_size; i++) {
+        if (wbuf[i] != rbuf[i]) {
+            printf("%s: byte %d is different: 0x%x != 0x%x, %p %p\n",
+                   __func__, i, wbuf[i], rbuf[i], &wbuf[i], &rbuf[i]);
+            hexdump(rbuf, chip->pagesize + chip->oobsize);
+            ret = ERR_IO;
+            goto freewbuf;
+        }
+    }
+
+freewbuf:
+    mempool_free(wbuf);
+freerbuf:
+    mempool_free(rbuf);
+
+    printf("%s: %u bit errors test %s\n", __func__, error_num,
+           ret < 0 ? "Failed" : "OK");
+
+    return ret;
+}
+
+static int nand_create_bbt(void)
+{
+    struct mtk_nand_chip *chip = mtk_get_nand_chip();
+    struct mtk_nand_ops ops;
+    u32 i, page;
+    int ret;
+
+    /* Free BBT */
+    free(chip->bbt);
+    chip->bbt = NULL;
+    chip->bbt_block = -1;
+
+    /* Erase BBT area */
+    for (i = 1; i <= SCAN_BBT_MAXBLOCKS; i++) {
+        page = chip->totalsize - chip->blocksize * i;
+        page /= chip->pagesize;
+        if (mtk_nand_block_isbad(chip, page))
+            continue;
+        memset(&ops, 0, sizeof(ops));
+        ops.mode = NAND_OPS_ERASE_POLL;
+        ops.offset = (u64)page * chip->pagesize;
+        ops.len = chip->blocksize;
+        mtk_nand_erase(chip, &ops);
+    }
+
+    ret = mtk_nand_scan_bbt(chip);
+
+    return ret;
+}
+
+static int cmd_nand(int argc, const cmd_args *argv)
+{
+    int ret = 0;
+
+    if (argc < 2) {
+notenoughargs:
+        printf("not enough arguments:\n");
+usage:
+        printf("%s info\n", argv[0].str);
+        printf("%s list\n", argv[0].str);
+        printf("%s speedtest <device>\n", argv[0].str);
+        printf("%s biterrstest <page> <error num>\n", argv[0].str);
+        printf("%s createbbt\n", argv[0].str);
+        return -1;
+    }
+
+    if (!strcmp(argv[1].str, "info")) {
+        nand_dump_device_info();
+    } else if (!strcmp(argv[1].str, "list")) {
+        bio_dump_devices();
+    } else if (!strcmp(argv[1].str, "speedtest")) {
+        if (argc < 3) goto notenoughargs;
+
+        ret = nand_speed_test(argv[2].str);
+    } else if (!strcmp(argv[1].str, "biterrstest")) {
+        if (argc < 4) goto notenoughargs;
+
+        ret = nand_bit_errors_test(argv[2].u, argv[3].u);
+    } else if (!strcmp(argv[1].str, "createbbt")) {
+        ret = nand_create_bbt();
+    } else {
+        printf("error: command %s not support\n", argv[1].str);
+        goto usage;
+    }
+
+    return ret;
+}
+
+STATIC_COMMAND_START
+STATIC_COMMAND("nand", "nand driver test & debug commands", &cmd_nand)
+STATIC_COMMAND_END(nand);
+
+#endif /* WITH_LIB_CONSOLE */
+
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/nand/mtk_nfi_hal.c b/src/bsp/lk/platform/mediatek/mt2712/drivers/nand/mtk_nfi_hal.c
new file mode 100644
index 0000000..f611781
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/nand/mtk_nfi_hal.c
@@ -0,0 +1,1772 @@
+/*
+ * Copyright (c) 2017 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <arch/ops.h>
+#include <errno.h>
+#include <kernel/event.h>
+#include <kernel/mutex.h>
+#include <kernel/vm.h>
+#include <malloc.h>
+#include <platform/interrupts.h>
+#include <platform/mt_irq.h>
+#include <platform/mt_reg_base.h>
+#include <platform/nand/mtk_ecc_hal.h>
+#include <platform/nand/mtk_nand_common.h>
+#include <platform/nand/mtk_nand_nal.h>
+#include <platform/nand/mtk_nfi_hal.h>
+#include <reg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+static inline struct mtk_nfc_nand_chip *to_mtk_nand(struct mtk_nand_chip *chip)
+{
+    return containerof(chip, struct mtk_nfc_nand_chip, chip);
+}
+
+static inline u8 *data_ptr(struct mtk_nand_chip *chip, const u8 *p, int i)
+{
+    return (u8 *)p + i * chip->ecc_size;
+}
+
+static inline int mtk_data_len(struct mtk_nand_chip *chip)
+{
+    struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+
+    return chip->ecc_size + mtk_nand->spare_per_sector;
+}
+
+static inline u8 *mtk_data_ptr(struct mtk_nand_chip *chip,  int i)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+
+    return nfc->buffer + i * mtk_data_len(chip);
+}
+
+static inline u8 *oob_ptr(struct mtk_nand_chip *chip, u32 i)
+{
+    struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+    u8 *poi;
+
+    /* map the sector's FDM data to free oob:
+     * the beginning of the oob area stores the FDM data of bad mark sectors
+     */
+    if (i < mtk_nand->bad_mark.sec)
+        poi = chip->oob_poi + (i + 1) * mtk_nand->fdm.reg_size;
+    else if (i == mtk_nand->bad_mark.sec)
+        poi = chip->oob_poi;
+    else
+        poi = chip->oob_poi + i * mtk_nand->fdm.reg_size;
+
+    return poi;
+}
+
+static inline u8 *oob_parity_ptr(struct mtk_nand_chip *chip, u32 i)
+{
+    struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+    u32 parity_size = mtk_nand->spare_per_sector - mtk_nand->fdm.reg_size;
+    u32 fdm_total_size = mtk_nand->fdm.reg_size * chip->ecc_steps;
+    u8 *poi;
+
+    poi = chip->oob_poi + fdm_total_size;
+
+    if (i < mtk_nand->bad_mark.sec)
+        poi += (i + 1) * parity_size;
+    else if (i > mtk_nand->bad_mark.sec)
+        poi += i * parity_size;
+
+    return poi;
+}
+
+static inline u8 *mtk_oob_ptr(struct mtk_nand_chip *chip, int i)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+
+    return nfc->buffer + i * mtk_data_len(chip) + chip->ecc_size;
+}
+
+static inline u8 *mtk_oob_parity_ptr(struct mtk_nand_chip *chip, int i)
+{
+    struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+
+    return mtk_oob_ptr(chip, i) + mtk_nand->fdm.reg_size; 
+}
+
+static inline void nfi_writel(struct mtk_nfc *nfc, u32 val, u32 reg)
+{
+    writel(val, nfc->regs + reg);
+
+    return;
+}
+
+static inline void nfi_writew(struct mtk_nfc *nfc, u16 val, u32 reg)
+{
+    (*REG16(nfc->regs + reg) = (val));
+
+    return;
+}
+
+static inline void nfi_writeb(struct mtk_nfc *nfc, u8 val, u32 reg)
+{
+    writeb(val, nfc->regs + reg);
+
+    return;
+}
+
+static inline u32 nfi_readl(struct mtk_nfc *nfc, u32 reg)
+{
+    return readl(nfc->regs + reg);
+}
+
+static inline u16 nfi_readw(struct mtk_nfc *nfc, u32 reg)
+{
+    return *REG16(nfc->regs + reg);
+}
+
+static inline u8 nfi_readb(struct mtk_nfc *nfc, u32 reg)
+{
+    return readb(nfc->regs + reg);
+}
+
+static void mtk_nfc_hw_reset(struct mtk_nfc *nfc)
+{
+    /* reset all registers and force the NFI master to terminate */
+    nfi_writel(nfc, CON_FIFO_FLUSH | CON_NFI_RST, NFI_CON);
+
+    /* wait for the master to finish the last transaction */
+    if (!check_with_timeout(!(nfi_readl(nfc, NFI_MASTER_STA) & MASTER_STA_MASK),
+                            MTK_RESET_TIMEOUT))
+        dprintf(CRITICAL, "NFI HW reset timeout!\n");
+
+    /* ensure any status register affected by the NFI master is reset */
+    nfi_writel(nfc, CON_FIFO_FLUSH | CON_NFI_RST, NFI_CON);
+    nfi_writew(nfc, STAR_DE, NFI_STRDATA);
+
+    return;
+}
+
+static int mtk_nfc_send_command(struct mtk_nfc *nfc, u8 command)
+{
+    nfi_writel(nfc, command, NFI_CMD);
+
+    if (!check_with_timeout(!(nfi_readl(nfc, NFI_STA) & STA_CMD), MTK_TIMEOUT))
+        dprintf(CRITICAL, "send cmd 0x%x timeout\n", command);
+
+    return 0;
+}
+
+static int mtk_nfc_send_address(struct mtk_nfc *nfc, int addr)
+{
+    nfi_writel(nfc, addr, NFI_COLADDR);
+    nfi_writel(nfc, 0, NFI_ROWADDR);
+    nfi_writew(nfc, 1, NFI_ADDRNOB);
+
+    if (!check_with_timeout(!(nfi_readl(nfc, NFI_STA) & STA_ADDR), MTK_TIMEOUT))
+        dprintf(CRITICAL, "send cmd 0x%x timeout\n", addr);
+
+    return 0;
+}
+
+static int mtk_nfc_irq_wait(struct mtk_nfc *nfc, lk_time_t timeout)
+{
+    int ret;
+
+    ret = event_wait_timeout(&nfc->irq_event, timeout);
+    if (ret != 0) {
+        dprintf(CRITICAL, "[%s]: failed to get event INT=0x%x\n",
+                __func__, nfi_readw(nfc, NFI_INTR_EN));
+        return ret;
+    }
+
+    return 0;
+}
+
+static enum handler_return mtk_nfc_interrupt_handler(void *arg)
+{
+    struct mtk_nfc *nfc = arg;
+    u16 sta, ien;
+
+    sta = nfi_readw(nfc, NFI_INTR_STA);
+    ien = nfi_readw(nfc, NFI_INTR_EN);
+    if (!(sta & ien))
+        return INT_NO_RESCHEDULE;
+
+    nfi_writew(nfc, ~sta & ien, NFI_INTR_EN);
+
+    /* MUST BE *false*! otherwise, schedule in interrupt */
+    event_signal(&nfc->irq_event, false);
+
+    return INT_RESCHEDULE;
+}
+
+static int mtk_nfc_request_irq(struct mtk_nfc *nfc)
+{
+    mt_irq_set_sens(NFI_IRQ_BIT_ID, LEVEL_SENSITIVE);
+    mt_irq_set_polarity(NFI_IRQ_BIT_ID, MT65xx_POLARITY_LOW);
+    event_init(&nfc->irq_event, false, EVENT_FLAG_AUTOUNSIGNAL);
+    register_int_handler(NFI_IRQ_BIT_ID, mtk_nfc_interrupt_handler, nfc);
+    unmask_interrupt(NFI_IRQ_BIT_ID);
+
+    return 0;
+}
+
+static int mtk_nfc_hw_runtime_config(struct mtk_nand_chip *chip)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+    u32 fmt, spare;
+
+    if (!chip->pagesize)
+        return -EINVAL;
+
+    spare = mtk_nand->spare_per_sector;
+
+    switch (chip->pagesize) {
+        case 512:
+            fmt = PAGEFMT_512_2K | PAGEFMT_SEC_SEL_512;
+            break;
+        case KB(2):
+            if (chip->ecc_size == 512)
+                fmt = PAGEFMT_2K_4K | PAGEFMT_SEC_SEL_512;
+            else
+                fmt = PAGEFMT_512_2K;
+            break;
+        case KB(4):
+            if (chip->ecc_size == 512)
+                fmt = PAGEFMT_4K_8K | PAGEFMT_SEC_SEL_512;
+            else
+                fmt = PAGEFMT_2K_4K;
+            break;
+        case KB(8):
+            if (chip->ecc_size == 512)
+                fmt = PAGEFMT_8K_16K | PAGEFMT_SEC_SEL_512;
+            else
+                fmt = PAGEFMT_4K_8K;
+            break;
+        case KB(16):
+            fmt = PAGEFMT_8K_16K;
+            break;
+        default:
+            dprintf(CRITICAL, "invalid page len: %d\n", chip->pagesize);
+            return -EINVAL;
+    }
+
+    /*
+     * the hardware will double the value for this eccsize, so we need to
+     * halve it
+     */
+    if (chip->ecc_size == 1024)
+        spare >>= 1;
+
+    switch (spare) {
+        case 16:
+            fmt |= (PAGEFMT_SPARE_16 << PAGEFMT_SPARE_SHIFT);
+            break;
+        case 26:
+            fmt |= (PAGEFMT_SPARE_26 << PAGEFMT_SPARE_SHIFT);
+            break;
+        case 27:
+            fmt |= (PAGEFMT_SPARE_27 << PAGEFMT_SPARE_SHIFT);
+            break;
+        case 28:
+            fmt |= (PAGEFMT_SPARE_28 << PAGEFMT_SPARE_SHIFT);
+            break;
+        case 32:
+            fmt |= (PAGEFMT_SPARE_32 << PAGEFMT_SPARE_SHIFT);
+            break;
+        case 36:
+            fmt |= (PAGEFMT_SPARE_36 << PAGEFMT_SPARE_SHIFT);
+            break;
+        case 40:
+            fmt |= (PAGEFMT_SPARE_40 << PAGEFMT_SPARE_SHIFT);
+            break;
+        case 44:
+            fmt |= (PAGEFMT_SPARE_44 << PAGEFMT_SPARE_SHIFT);
+            break;
+        case 48:
+            fmt |= (PAGEFMT_SPARE_48 << PAGEFMT_SPARE_SHIFT);
+            break;
+        case 49:
+            fmt |= (PAGEFMT_SPARE_49 << PAGEFMT_SPARE_SHIFT);
+            break;
+        case 50:
+            fmt |= (PAGEFMT_SPARE_50 << PAGEFMT_SPARE_SHIFT);
+            break;
+        case 51:
+            fmt |= (PAGEFMT_SPARE_51 << PAGEFMT_SPARE_SHIFT);
+            break;
+        case 52:
+            fmt |= (PAGEFMT_SPARE_52 << PAGEFMT_SPARE_SHIFT);
+            break;
+        case 62:
+            fmt |= (PAGEFMT_SPARE_62 << PAGEFMT_SPARE_SHIFT);
+            break;
+        case 63:
+            fmt |= (PAGEFMT_SPARE_63 << PAGEFMT_SPARE_SHIFT);
+            break;
+        case 64:
+            fmt |= (PAGEFMT_SPARE_64 << PAGEFMT_SPARE_SHIFT);
+            break;
+        default:
+            dprintf(CRITICAL, "invalid spare per sector %d\n", spare);
+            return -EINVAL;
+    }
+
+    fmt |= mtk_nand->fdm.reg_size << PAGEFMT_FDM_SHIFT;
+    fmt |= mtk_nand->fdm.ecc_size << PAGEFMT_FDM_ECC_SHIFT;
+    nfi_writel(nfc, fmt, NFI_PAGEFMT);
+
+    nfc->ecc_cfg.strength = chip->ecc_strength;
+    nfc->ecc_cfg.len = chip->ecc_size + mtk_nand->fdm.ecc_size;
+
+    return 0;
+}
+
+static void mtk_nfc_select_chip(struct mtk_nand_chip *chip, int chip_num)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+
+    if ((chip_num < 0) || (chip_num == chip->activechip))
+        return;
+
+    if (!mtk_nfc_hw_runtime_config(chip)) {
+        chip->activechip = chip_num;
+    }
+
+    nfi_writel(nfc, chip_num, NFI_CSEL);
+
+    return;
+}
+
+static int mtk_nfc_dev_ready(struct mtk_nand_chip *chip)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+
+    if (nfi_readl(nfc, NFI_STA) & STA_BUSY)
+        return 0;
+
+    return 1;
+}
+
+static int mtk_nfc_wait_busy_irq(struct mtk_nand_chip *chip)
+{
+    int ret;
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+
+    /* set wait busy interrupt */
+    nfi_writew(nfc, INTR_BUSY_RETURN_EN, NFI_INTR_EN);
+
+    /* wait interrupt */
+    ret = mtk_nfc_irq_wait(nfc, MTK_TIMEOUT);
+    if (!ret) {
+        dprintf(CRITICAL, "wait busy interrupt timeout\n");
+        nfi_writew(nfc, 0, NFI_INTR_EN);
+        return -ETIMEDOUT;
+    }
+
+    return 0;
+}
+
+static void mtk_nfc_cmd_ctrl(struct mtk_nand_chip *chip, int dat, unsigned int ctrl)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    u16 reg;
+
+    if (ctrl & NAND_ALE) {
+        mtk_nfc_send_address(nfc, dat);
+    } else if (ctrl & NAND_CLE) {
+        mtk_nfc_hw_reset(nfc);
+
+        reg = nfi_readw(nfc, NFI_CNFG);
+        reg &= CNFG_RAND_MASK;
+        reg |= CNFG_OP_CUST;
+        nfi_writew(nfc, reg, NFI_CNFG);
+        mtk_nfc_send_command(nfc, dat);
+    }
+
+    return;
+}
+
+static inline void mtk_nfc_wait_ioready(struct mtk_nfc *nfc)
+{
+    if (!check_with_timeout((nfi_readl(nfc, NFI_PIO_DIRDY) & PIO_DI_RDY), MTK_TIMEOUT)) {
+        dprintf(CRITICAL, "data not ready\n");
+        dprintf(CRITICAL, "cntr 0x%x cnfg 0x%x fmt 0x%x con 0x%x\n", nfi_readl(nfc, NFI_BYTELEN),
+                nfi_readl(nfc, NFI_CNFG), nfi_readl(nfc, NFI_PAGEFMT), nfi_readl(nfc, NFI_CON));
+    }
+
+    return;
+}
+
+static inline u8 mtk_nfc_read_byte(struct mtk_nand_chip *chip)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    u32 reg;
+
+    /* after each byte read, the NFI_STA reg is reset by the hardware */
+    reg = nfi_readl(nfc, NFI_STA) & NFI_FSM_MASK;
+    if (reg != NFI_FSM_CUSTDATA) {
+        reg = nfi_readw(nfc, NFI_CNFG);
+        reg |= CNFG_BYTE_RW | CNFG_READ_EN;
+        nfi_writew(nfc, reg, NFI_CNFG);
+
+        /*
+         * set to max sector to allow the HW to continue reading over
+         * unaligned accesses
+         */
+        reg = (MTK_MAX_SECTOR << CON_SEC_SHIFT) | CON_BRD;
+        nfi_writel(nfc, reg, NFI_CON);
+
+        /* trigger to fetch data */
+        nfi_writew(nfc, STAR_EN, NFI_STRDATA);
+    }
+
+    mtk_nfc_wait_ioready(nfc);
+
+    return nfi_readb(nfc, NFI_DATAR);
+}
+
+static void mtk_nfc_read_buf(struct mtk_nand_chip *chip, u8 *buf, int len)
+{
+    int i;
+
+    for (i = 0; i < len; i++)
+        buf[i] = mtk_nfc_read_byte(chip);
+
+    return;
+}
+
+static void mtk_nfc_write_byte(struct mtk_nand_chip *chip, u8 byte)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    u32 reg;
+
+    reg = nfi_readl(nfc, NFI_STA) & NFI_FSM_MASK;
+
+    if (reg != NFI_FSM_CUSTDATA) {
+        reg = nfi_readw(nfc, NFI_CNFG) | CNFG_BYTE_RW;
+        nfi_writew(nfc, reg, NFI_CNFG);
+
+        reg = MTK_MAX_SECTOR << CON_SEC_SHIFT | CON_BWR;
+        nfi_writel(nfc, reg, NFI_CON);
+
+        nfi_writew(nfc, STAR_EN, NFI_STRDATA);
+    }
+
+    mtk_nfc_wait_ioready(nfc);
+    nfi_writeb(nfc, byte, NFI_DATAW);
+
+    return;
+}
+
+static void mtk_nfc_write_buf(struct mtk_nand_chip *chip, const u8 *buf, int len)
+{
+    int i;
+
+    for (i = 0; i < len; i++)
+        mtk_nfc_write_byte(chip, buf[i]);
+
+    return;
+}
+
+static int mtk_nfc_sector_encode(struct mtk_nand_chip *chip, u8 *data, int dma, int polling)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+    int size = chip->ecc_size + mtk_nand->fdm.reg_size;
+
+    if (dma)
+        nfc->ecc_cfg.mode = ECC_DMA_MODE;
+    else
+        nfc->ecc_cfg.mode = ECC_PIO_MODE;
+    nfc->ecc_cfg.op = ECC_ENCODE;
+
+    return mtk_ecc_encode(nfc->ecc, &nfc->ecc_cfg, data, size, polling);
+}
+
+static void mtk_nfc_no_bad_mark_swap(struct mtk_nand_chip *a, u8 *b, int c)
+{
+    /* nop */
+    return;
+}
+
+static void mtk_nfc_bad_mark_swap(struct mtk_nand_chip *chip, u8 *buf, int raw)
+{
+    struct mtk_nfc_nand_chip *nand = to_mtk_nand(chip);
+    u32 bad_pos = nand->bad_mark.pos;
+
+    if (raw)
+        bad_pos += nand->bad_mark.sec * mtk_data_len(chip);
+    else
+        bad_pos += nand->bad_mark.sec * chip->ecc_size;
+
+    swap(chip->oob_poi[0], buf[bad_pos]);
+
+    return;
+}
+
+static int mtk_nfc_format_subpage(struct mtk_nand_chip *chip, u32 offset,
+                                  u32 len, const u8 *buf, int dma, int polling)
+{
+    struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
+    u32 start, end, i;
+    int ret;
+
+    start = offset / chip->ecc_size;
+    end = DIV_ROUND_UP(offset + len, chip->ecc_size);
+
+    memset(nfc->buffer, 0xff, chip->pagesize + chip->oobsize);
+    for (i = 0; i < chip->ecc_steps; i++) {
+        memcpy(mtk_data_ptr(chip, i), data_ptr(chip, buf, i), chip->ecc_size);
+
+        if (start > i || i >= end)
+            continue;
+
+        if (i == mtk_nand->bad_mark.sec)
+            mtk_nand->bad_mark.bm_swap(chip, nfc->buffer, 1);
+
+        memcpy(mtk_oob_ptr(chip, i), oob_ptr(chip, i), fdm->reg_size);
+
+        /* program the CRC back to the OOB */
+        ret = mtk_nfc_sector_encode(chip, mtk_data_ptr(chip, i), dma, polling);
+        if (ret < 0)
+            return ret;
+    }
+
+    return 0;
+}
+
+static void mtk_nfc_format_page(struct mtk_nand_chip *chip, const u8 *buf)
+{
+    struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
+    u32 parity_size = mtk_nand->spare_per_sector - fdm->reg_size;
+    u32 i;
+
+    memset(nfc->buffer, 0xff, chip->pagesize + chip->oobsize);
+    for (i = 0; i < chip->ecc_steps; i++) {
+        if (buf)
+            memcpy(mtk_data_ptr(chip, i), data_ptr(chip, buf, i),
+                   chip->ecc_size);
+
+        if (i == mtk_nand->bad_mark.sec)
+            mtk_nand->bad_mark.bm_swap(chip, nfc->buffer, 1);
+
+        memcpy(mtk_oob_ptr(chip, i), oob_ptr(chip, i), fdm->reg_size);
+        memcpy(mtk_oob_parity_ptr(chip, i), oob_parity_ptr(chip, i),
+               parity_size);
+    }
+
+    return;
+}
+
+static inline void mtk_nfc_read_fdm(struct mtk_nand_chip *chip, u32 start,
+                                    u32 sectors)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+    struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
+    u32 vall, valm, i, j;
+    u8 *oobptr;
+
+    for (i = 0; i < sectors; i++) {
+        oobptr = oob_ptr(chip, start + i);
+        vall = nfi_readl(nfc, NFI_FDML(i));
+        valm = nfi_readl(nfc, NFI_FDMM(i));
+
+        for (j = 0; j < fdm->reg_size; j++)
+            oobptr[j] = (j >= 4 ? valm : vall) >> ((j % 4) * 8);
+    }
+
+    return;
+}
+
+static inline void mtk_nfc_write_fdm(struct mtk_nand_chip *chip)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+    struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
+    u32 vall, valm, i, j;
+    u8 *oobptr;
+
+    for (i = 0; i < chip->ecc_steps; i++) {
+        oobptr = oob_ptr(chip, i);
+        vall = 0;
+        valm = 0;
+        for (j = 0; j < 8; j++) {
+            if (j < 4)
+                vall |= (j < fdm->reg_size ? oobptr[j] : 0xff)
+                        << (j * 8);
+            else
+                valm |= (j < fdm->reg_size ? oobptr[j] : 0xff)
+                        << ((j - 4) * 8);
+        }
+        nfi_writel(nfc, vall, NFI_FDML(i));
+        nfi_writel(nfc, valm, NFI_FDMM(i));
+    }
+
+    return;
+}
+
+static int mtk_nfc_do_write_page(struct mtk_nand_chip *chip,
+                                 const u8 *buf, int page, int len, int raw, int dma, int polling)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    u32 *buf32 = (u32 *)buf;
+    uintptr_t addr;
+    u32 reg, i;
+    u32 data_len = chip->ecc_size;
+    int ret = 0, byterw;
+
+#ifdef WITH_KERNEL_VM
+    addr = (uintptr_t)kvaddr_to_paddr((void *)buf);
+#else
+    addr = (uintptr_t)buf;
+#endif
+
+    if (dma) {
+        reg = nfi_readw(nfc, NFI_CNFG) | CNFG_AHB | CNFG_DMA_BURST_EN;
+        nfi_writew(nfc, reg, NFI_CNFG);
+        arch_clean_cache_range((addr_t)buf, (size_t)len);
+    }
+    nfi_writel(nfc, chip->ecc_steps << CON_SEC_SHIFT, NFI_CON);
+    nfi_writel(nfc, (u32)addr, NFI_STRADDR);
+
+    if (dma && (!polling)) {
+        nfi_writew(nfc, INTR_AHB_DONE_EN, NFI_INTR_EN);
+    }
+
+    reg = nfi_readl(nfc, NFI_CON) | CON_BWR;
+    nfi_writel(nfc, reg, NFI_CON);
+    nfi_writew(nfc, STAR_EN, NFI_STRDATA);
+
+    if (!dma) {
+        if (raw)
+            data_len = mtk_data_len(chip);
+        data_len *= chip->ecc_steps;
+
+        if (data_len & 0x3) {
+            reg = nfi_readw(nfc, NFI_CNFG) | CNFG_BYTE_RW;
+            nfi_writew(nfc, reg, NFI_CNFG);
+            byterw = 1;
+        } else {
+            data_len >>= 2;
+            byterw = 0;
+        }
+
+        for (i = 0; i < data_len; i++) {
+            mtk_nfc_wait_ioready(nfc);
+            if (!byterw)
+                nfi_writel(nfc, buf32[i],NFI_DATAW);
+            else
+                nfi_writeb(nfc, buf[i], NFI_DATAW);
+        }
+    }
+
+    if (dma && (!polling)) {
+        ret = mtk_nfc_irq_wait(nfc, MTK_TIMEOUT);
+        if (!ret) {
+            dprintf(CRITICAL, "program ahb done timeout\n");
+            nfi_writew(nfc, 0, NFI_INTR_EN);
+            ret = -ETIMEDOUT;
+            goto timeout;
+        }
+    }
+
+    if (!check_with_timeout(ADDRCNTR_SEC(nfi_readl(nfc, NFI_ADDRCNTR)) >= chip->ecc_steps, MTK_TIMEOUT))
+        dprintf(CRITICAL, "do page write timeout\n");
+
+timeout:
+    if (dma)
+        arch_invalidate_cache_range((addr_t)buf, (size_t)len);
+
+    nfi_writel(nfc, 0, NFI_CON);
+
+    return ret;
+}
+
+static int mtk_nfc_write_page(struct mtk_nand_chip *chip,
+                              const u8 *buf, int page, int raw, int dma, int polling)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+    u32 len;
+    const u8 *bufpoi;
+    u32 reg;
+    int ret;
+
+    if (!raw) {
+        /* OOB => FDM: from register,  ECC: from HW */
+        reg = nfi_readw(nfc, NFI_CNFG) | CNFG_AUTO_FMT_EN;
+        nfi_writew(nfc, reg | CNFG_HW_ECC_EN, NFI_CNFG);
+
+        nfc->ecc_cfg.op = ECC_ENCODE;
+        nfc->ecc_cfg.mode = ECC_NFI_MODE;
+        ret = mtk_ecc_enable(nfc->ecc, &nfc->ecc_cfg, polling);
+        if (ret) {
+            /* clear NFI config */
+            reg = nfi_readw(nfc, NFI_CNFG);
+            reg &= ~(CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN);
+            nfi_writew(nfc, reg, NFI_CNFG);
+
+            return ret;
+        }
+
+        memcpy(nfc->buffer, buf, chip->pagesize);
+        mtk_nand->bad_mark.bm_swap(chip, nfc->buffer, raw);
+        bufpoi = nfc->buffer;
+
+        /* write OOB into the FDM registers (OOB area in MTK NAND) */
+        mtk_nfc_write_fdm(chip);
+    } else {
+        bufpoi = buf;
+    }
+
+    len = chip->pagesize + (raw ? chip->oobsize : 0);
+    ret = mtk_nfc_do_write_page(chip, bufpoi, page, len, raw, dma, polling);
+
+    if (!raw)
+        mtk_ecc_disable(nfc->ecc);
+
+    return ret;
+}
+
+static int mtk_nfc_write_page_ecc_dma_polling(struct mtk_nand_chip *chip, const u8 *buf,
+        int page)
+{
+    return mtk_nfc_write_page(chip, buf, page, 0, 1, 1);
+}
+
+static int mtk_nfc_write_page_ecc_dma_irq(struct mtk_nand_chip *chip, const u8 *buf,
+        int page)
+{
+    return mtk_nfc_write_page(chip, buf, page, 0, 1, 0);
+}
+
+static int mtk_nfc_write_page_ecc_pio_polling(struct mtk_nand_chip *chip, const u8 *buf,
+        int page)
+{
+    return mtk_nfc_write_page(chip, buf, page, 0, 0, 1);
+}
+
+static int mtk_nfc_write_page_ecc_pio_irq(struct mtk_nand_chip *chip, const u8 *buf,
+        int page)
+{
+    return mtk_nfc_write_page(chip, buf, page, 0, 0, 0);
+}
+
+static int mtk_nfc_write_page_raw_dma_polling(struct mtk_nand_chip *chip,
+        const u8 *buf, int pg)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+
+    mtk_nfc_format_page(chip, buf);
+    return mtk_nfc_write_page(chip, nfc->buffer, pg, 1, 1, 1);
+}
+
+static int mtk_nfc_write_page_raw_dma_irq(struct mtk_nand_chip *chip,
+        const u8 *buf, int pg)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+
+    mtk_nfc_format_page(chip, buf);
+    return mtk_nfc_write_page(chip, nfc->buffer, pg, 1, 1, 0);
+}
+
+static int mtk_nfc_write_page_raw_pio_polling(struct mtk_nand_chip *chip,
+        const u8 *buf, int pg)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+
+    mtk_nfc_format_page(chip, buf);
+    return mtk_nfc_write_page(chip, nfc->buffer, pg, 1, 0, 1);
+}
+
+static int mtk_nfc_write_page_raw_pio_irq(struct mtk_nand_chip *chip,
+        const u8 *buf, int pg)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+
+    mtk_nfc_format_page(chip, buf);
+    return mtk_nfc_write_page(chip, nfc->buffer, pg, 1, 0, 0);
+}
+
+static int mtk_nfc_write_subpage_ecc_dma_polling(struct mtk_nand_chip *chip, u32 offset,
+        u32 data_len, const u8 *buf, int page)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    int ret;
+
+    ret = mtk_nfc_format_subpage(chip, offset, data_len, buf, 1, 1);
+    if (ret < 0)
+        return ret;
+
+    /* use the data in the private buffer (now with FDM and CRC) */
+    return mtk_nfc_write_page(chip, nfc->buffer, page, 1, 1, 1);
+}
+
+static int mtk_nfc_write_subpage_ecc_dma_irq(struct mtk_nand_chip *chip, u32 offset,
+        u32 data_len, const u8 *buf, int page)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    int ret;
+
+    ret = mtk_nfc_format_subpage(chip, offset, data_len, buf, 1, 0);
+    if (ret < 0)
+        return ret;
+
+    /* use the data in the private buffer (now with FDM and CRC) */
+    return mtk_nfc_write_page(chip, nfc->buffer, page, 1, 1, 0);
+}
+
+static int mtk_nfc_write_subpage_ecc_pio_polling(struct mtk_nand_chip *chip, u32 offset,
+        u32 data_len, const u8 *buf, int page)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    int ret;
+
+    ret = mtk_nfc_format_subpage(chip, offset, data_len, buf, 0, 1);
+    if (ret < 0)
+        return ret;
+
+    /* use the data in the private buffer (now with FDM and CRC) */
+    return mtk_nfc_write_page(chip, nfc->buffer, page, 1, 0, 1);
+}
+
+static int mtk_nfc_write_subpage_ecc_pio_irq(struct mtk_nand_chip *chip, u32 offset,
+        u32 data_len, const u8 *buf, int page)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    int ret;
+
+    ret = mtk_nfc_format_subpage(chip, offset, data_len, buf, 0, 0);
+    if (ret < 0)
+        return ret;
+
+    /* use the data in the private buffer (now with FDM and CRC) */
+    return mtk_nfc_write_page(chip, nfc->buffer, page, 1, 0, 0);
+}
+
+static int mtk_nfc_update_ecc_stats(struct mtk_nand_chip *chip, u8 *buf, u32 sectors)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+    struct mtk_ecc_stats stats;
+    u32 rc, i;
+
+    rc = nfi_readl(nfc, NFI_STA) & STA_EMP_PAGE;
+    if (rc) {
+        memset(buf, 0xff, sectors * chip->ecc_size);
+        for (i = 0; i < sectors; i++)
+            memset(oob_ptr(chip, i), 0xff, mtk_nand->fdm.reg_size);
+        return 0;
+    }
+
+    mtk_ecc_get_stats(nfc->ecc, &stats, sectors);
+    chip->stats.corrected += stats.corrected;
+    chip->stats.failed += stats.failed;
+
+    return stats.bitflips;
+}
+
+static int mtk_nfc_read_subpage(struct mtk_nand_chip *chip,
+                                u32 data_offs, u32 readlen,
+                                u8 *bufpoi, int page, int raw, int dma, int polling)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+    struct mtk_ecc_stats stats;
+    u32 spare = mtk_nand->spare_per_sector;
+    u32 column, sectors, start, end, reg;
+    uintptr_t addr;
+    u32 i, j;
+    int bitflips = 0;
+    u32 len, correct = 0, fail = 0;
+    u8 *buf;
+    u32 *buf32;
+    u32 data_len = chip->ecc_size;
+    int rc, byterw;
+
+    start = data_offs / chip->ecc_size;
+    end = DIV_ROUND_UP(data_offs + readlen, chip->ecc_size);
+
+    sectors = end - start;
+    column = start * (chip->ecc_size + spare);
+
+    len = sectors * chip->ecc_size + ((raw || !dma) ? sectors * spare : 0);
+    buf = bufpoi + start * (chip->ecc_size + ((raw || !dma) ? sectors * spare : 0));
+    buf32 = (u32 *)buf;
+
+    if (column != 0)
+        chip->cmdfunc(chip, NAND_CMD_RNDOUT, column, -1);
+
+#ifdef WITH_KERNEL_VM
+    addr = (uintptr_t)kvaddr_to_paddr(buf);
+#else
+    addr = (uintptr_t)buf;
+#endif
+
+    reg = nfi_readw(nfc, NFI_CNFG);
+    reg |= CNFG_READ_EN;
+    if (dma)
+        reg |= CNFG_DMA_BURST_EN | CNFG_AHB;
+    if (!raw) {
+        reg |= CNFG_HW_ECC_EN;
+        if (dma)
+            reg |= CNFG_AUTO_FMT_EN;
+        nfi_writew(nfc, reg, NFI_CNFG);
+
+        nfc->ecc_cfg.mode = ECC_NFI_MODE;
+        nfc->ecc_cfg.sectors = sectors;
+        nfc->ecc_cfg.op = ECC_DECODE;
+        if (dma) {
+            nfc->ecc_cfg.deccon = ECC_DEC_CORRECT;
+        } else {
+            nfc->ecc_cfg.deccon = ECC_DEC_LOCATE;
+        }
+        rc = mtk_ecc_enable(nfc->ecc, &nfc->ecc_cfg, polling);
+        if (rc) {
+            dprintf(CRITICAL, "ecc enable failed\n");
+            /* clear NFI_CNFG */
+            reg &= ~(CNFG_DMA_BURST_EN | CNFG_AHB | CNFG_READ_EN |
+                     CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN);
+            nfi_writew(nfc, reg, NFI_CNFG);
+
+            return rc;
+        }
+    } else {
+        nfi_writew(nfc, reg, NFI_CNFG);
+    }
+
+    if (dma)
+        arch_invalidate_cache_range((addr_t)buf, (size_t)len);
+    nfi_writel(nfc, sectors << CON_SEC_SHIFT, NFI_CON);
+    nfi_writel(nfc, (u32)addr, NFI_STRADDR);
+
+    if (dma && (!polling)) {
+        nfi_writew(nfc, INTR_AHB_DONE_EN, NFI_INTR_EN);
+    }
+    reg = nfi_readl(nfc, NFI_CON) | CON_BRD;
+    nfi_writel(nfc, reg, NFI_CON);
+    nfi_writew(nfc, STAR_EN, NFI_STRDATA);
+
+    if (!dma) {
+        data_len = mtk_data_len(chip);
+
+        if (data_len & 0x3) {
+            reg = nfi_readw(nfc, NFI_CNFG) | CNFG_BYTE_RW;
+            nfi_writew(nfc, reg, NFI_CNFG);
+            byterw = 1;
+        } else {
+            data_len >>= 2;
+            byterw = 0;
+        }
+        if (!raw) {
+            stats.bitflips = 0;
+            correct = chip->stats.corrected;
+            fail = chip->stats.failed;
+        }
+        for (i = 0; i < sectors; i++) {
+            for (j = 0; j < data_len; j++) {
+                mtk_nfc_wait_ioready(nfc);
+                if (!byterw)
+                    *(buf32 + (i * data_len) + j) = nfi_readl(nfc, NFI_DATAR);
+                else
+                    *(buf + (i * data_len) + j) = nfi_readb(nfc, NFI_DATAR);
+            }
+            if (!raw) {
+                rc = mtk_ecc_cpu_correct(nfc->ecc, &stats, buf + (i * (byterw ? data_len : (data_len << 2))), i, polling);
+                if (rc < 0)
+                    goto disecc;
+                chip->stats.corrected += stats.corrected;
+                chip->stats.failed += stats.failed;
+                if (stats.failed)
+                    dprintf(ALWAYS, "sectoer %d uncorrect\n", i);
+            }
+        }
+        if (!raw) {
+            bitflips = stats.bitflips;
+            rc = nfi_readl(nfc, NFI_STA) & STA_EMP_PAGE;
+            if (rc) {
+                LTRACEF("page is empty\n");
+                memset(buf, 0xff, sectors * mtk_data_len(chip));
+                bitflips = 0;
+                chip->stats.corrected = correct;
+                chip->stats.failed = fail;
+            }
+        }
+    }
+
+    if (dma && (!polling)) {
+        rc = mtk_nfc_irq_wait(nfc, MTK_TIMEOUT);
+        if (!rc) {
+            dprintf(CRITICAL, "read ahb/dma done timeout\n");
+            nfi_writew(nfc, 0, NFI_INTR_EN);
+        }
+    }
+
+    rc = check_with_timeout(ADDRCNTR_SEC(nfi_readl(nfc, NFI_BYTELEN)) >= sectors, MTK_TIMEOUT);
+    if (rc && polling)
+        rc = check_with_timeout((nfi_readl(nfc, NFI_MASTER_STA) & MASTER_BUS_BUSY) == 0, MTK_TIMEOUT);
+    if (!rc) {
+        dprintf(CRITICAL, "subpage done timeout %d\n", nfi_readl(nfc, NFI_BYTELEN));
+        dprintf(CRITICAL, "cnfg 0x%x fmt 0x%x con 0x%x master_sta 0x%x\n",
+                nfi_readl(nfc, NFI_CNFG), nfi_readl(nfc, NFI_PAGEFMT), nfi_readl(nfc, NFI_CON),
+                nfi_readl(nfc, NFI_MASTER_STA));
+        bitflips = -EIO;
+    } else {
+        if ((!raw) && dma) {
+            bitflips = 0;
+            rc = mtk_ecc_wait_done(nfc->ecc, ECC_DECODE, polling);
+            if (!rc)
+                rc = mtk_ecc_wait_decode_fsm_idle(nfc->ecc);
+            arch_invalidate_cache_range((addr_t)buf, (size_t)len);
+            mtk_nfc_read_fdm(chip, start, sectors);
+            bitflips = rc < 0 ? -ETIMEDOUT :
+                       mtk_nfc_update_ecc_stats(chip, buf, sectors);
+        }
+    }
+
+    if (raw)
+        goto done;
+
+disecc:
+    mtk_ecc_disable(nfc->ecc);
+
+    if (!dma)
+        goto done;
+
+    if (clamp(mtk_nand->bad_mark.sec, start, end) == mtk_nand->bad_mark.sec)
+        mtk_nand->bad_mark.bm_swap(chip, bufpoi, raw);
+done:
+    nfi_writel(nfc, 0, NFI_CON);
+
+    return bitflips;
+}
+
+static int mtk_nfc_read_subpage_ecc_dma_polling(struct mtk_nand_chip *chip, u32 off,
+        u32 len, u8 *p, int pg)
+{
+    return mtk_nfc_read_subpage(chip, off, len, p, pg, 0, 1, 1);
+}
+
+static int mtk_nfc_read_subpage_ecc_dma_irq(struct mtk_nand_chip *chip, u32 off,
+        u32 len, u8 *p, int pg)
+{
+    return mtk_nfc_read_subpage(chip, off, len, p, pg, 0, 1, 0);
+}
+
+static int mtk_nfc_read_subpage_ecc_pio_polling(struct mtk_nand_chip *chip, u32 off,
+        u32 len, u8 *p, int pg)
+{
+    struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
+    u32 start, end, i;
+    int ret;
+
+    start = off / chip->ecc_size;
+    end = DIV_ROUND_UP(off + len, chip->ecc_size);
+
+    memset(nfc->buffer, 0xff, chip->pagesize + chip->oobsize);
+    ret = mtk_nfc_read_subpage(chip, off, len, nfc->buffer, pg, 0, 0, 1);
+    if (ret < 0)
+        return ret;
+
+    for (i = start; i < end; i++) {
+        memcpy(oob_ptr(chip, i), mtk_oob_ptr(chip, i), fdm->reg_size);
+
+        if (i == mtk_nand->bad_mark.sec)
+            mtk_nand->bad_mark.bm_swap(chip, nfc->buffer, 1);
+
+        if (p)
+            memcpy(data_ptr(chip, p, i), mtk_data_ptr(chip, i),
+                   chip->ecc_size);
+    }
+
+    return ret;
+}
+
+static int mtk_nfc_read_subpage_ecc_pio_irq(struct mtk_nand_chip *chip, u32 off,
+        u32 len, u8 *p, int pg)
+{
+    struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
+    u32 start, end, i;
+    int ret;
+
+    start = off / chip->ecc_size;
+    end = DIV_ROUND_UP(off + len, chip->ecc_size);
+
+    memset(nfc->buffer, 0xff, chip->pagesize + chip->oobsize);
+    ret = mtk_nfc_read_subpage(chip, off, len, nfc->buffer, pg, 0, 0, 0);
+    if (ret < 0)
+        return ret;
+
+    for (i = start; i < end; i++) {
+        memcpy(oob_ptr(chip, i), mtk_oob_ptr(chip, i), fdm->reg_size);
+
+        if (i == mtk_nand->bad_mark.sec)
+            mtk_nand->bad_mark.bm_swap(chip, nfc->buffer, 1);
+
+        if (p)
+            memcpy(data_ptr(chip, p, i), mtk_data_ptr(chip, i),
+                   chip->ecc_size);
+    }
+
+    return ret;
+}
+
+static int mtk_nfc_read_page_ecc_dma_polling(struct mtk_nand_chip *chip, u8 *p,
+        int pg)
+{
+    return mtk_nfc_read_subpage(chip, 0, chip->pagesize, p, pg, 0, 1, 1);
+}
+
+static int mtk_nfc_read_page_ecc_dma_irq(struct mtk_nand_chip *chip, u8 *p,
+        int pg)
+{
+    return mtk_nfc_read_subpage(chip, 0, chip->pagesize, p, pg, 0, 1, 0);
+}
+
+static int mtk_nfc_read_page_ecc_pio_polling(struct mtk_nand_chip *chip, u8 *p,
+        int pg)
+{
+    struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
+    u32 i;
+    int ret;
+
+    memset(nfc->buffer, 0xff, chip->pagesize + chip->oobsize);
+    ret = mtk_nfc_read_subpage(chip, 0, chip->pagesize, nfc->buffer, pg, 0, 0, 1);
+    if (ret < 0)
+        return ret;
+
+    for (i = 0; i < chip->ecc_steps; i++) {
+        memcpy(oob_ptr(chip, i), mtk_oob_ptr(chip, i), fdm->reg_size);
+
+        if (i == mtk_nand->bad_mark.sec)
+            mtk_nand->bad_mark.bm_swap(chip, nfc->buffer, 1);
+
+        if (p)
+            memcpy(data_ptr(chip, p, i), mtk_data_ptr(chip, i),
+                   chip->ecc_size);
+    }
+
+    return ret;
+}
+
+static int mtk_nfc_read_page_ecc_pio_irq(struct mtk_nand_chip *chip, u8 *p,
+        int pg)
+{
+    struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
+    u32 i;
+    int ret;
+
+    memset(nfc->buffer, 0xff, chip->pagesize + chip->oobsize);
+    ret = mtk_nfc_read_subpage(chip, 0, chip->pagesize, nfc->buffer, pg, 0, 0, 0);
+    if (ret < 0)
+        return ret;
+
+    for (i = 0; i < chip->ecc_steps; i++) {
+        memcpy(oob_ptr(chip, i), mtk_oob_ptr(chip, i), fdm->reg_size);
+
+        if (i == mtk_nand->bad_mark.sec)
+            mtk_nand->bad_mark.bm_swap(chip, nfc->buffer, 1);
+
+        if (p)
+            memcpy(data_ptr(chip, p, i), mtk_data_ptr(chip, i),
+                   chip->ecc_size);
+    }
+
+    return ret;
+}
+
+static int mtk_nfc_read_page_raw_dma_polling(struct mtk_nand_chip *chip,
+        u8 *buf, int page)
+{
+    struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
+    u32 i;
+    u32 parity_size = mtk_nand->spare_per_sector - fdm->reg_size;
+    int ret;
+
+    memset(nfc->buffer, 0xff, chip->pagesize + chip->oobsize);
+    ret = mtk_nfc_read_subpage(chip, 0, chip->pagesize, nfc->buffer,
+                               page, 1, 1, 1);
+    if (ret < 0)
+        return ret;
+
+    for (i = 0; i < chip->ecc_steps; i++) {
+        memcpy(oob_ptr(chip, i), mtk_oob_ptr(chip, i), fdm->reg_size);
+        memcpy(oob_parity_ptr(chip, i), mtk_oob_parity_ptr(chip, i),
+               parity_size);
+
+        if (i == mtk_nand->bad_mark.sec)
+            mtk_nand->bad_mark.bm_swap(chip, nfc->buffer, 1);
+
+        if (buf)
+            memcpy(data_ptr(chip, buf, i), mtk_data_ptr(chip, i),
+                   chip->ecc_size);
+    }
+
+    return ret;
+}
+
+static int mtk_nfc_read_page_raw_dma_irq(struct mtk_nand_chip *chip,
+        u8 *buf, int page)
+{
+    struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
+    u32 i;
+    u32 parity_size = mtk_nand->spare_per_sector - fdm->reg_size;
+    int ret;
+
+    memset(nfc->buffer, 0xff, chip->pagesize + chip->oobsize);
+    ret = mtk_nfc_read_subpage(chip, 0, chip->pagesize, nfc->buffer,
+                               page, 1, 1, 0);
+    if (ret < 0)
+        return ret;
+
+    for (i = 0; i < chip->ecc_steps; i++) {
+        memcpy(oob_ptr(chip, i), mtk_oob_ptr(chip, i), fdm->reg_size);
+        memcpy(oob_parity_ptr(chip, i), mtk_oob_parity_ptr(chip, i),
+               parity_size);
+
+        if (i == mtk_nand->bad_mark.sec)
+            mtk_nand->bad_mark.bm_swap(chip, nfc->buffer, 1);
+
+        if (buf)
+            memcpy(data_ptr(chip, buf, i), mtk_data_ptr(chip, i),
+                   chip->ecc_size);
+    }
+
+    return ret;
+}
+
+static int mtk_nfc_read_page_raw_pio_polling(struct mtk_nand_chip *chip,
+        u8 *buf, int page)
+{
+    struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
+    u32 i;
+    u32 parity_size = mtk_nand->spare_per_sector - fdm->reg_size;
+    int ret;
+
+    memset(nfc->buffer, 0xff, chip->pagesize + chip->oobsize);
+    ret = mtk_nfc_read_subpage(chip, 0, chip->pagesize, nfc->buffer,
+                               page, 1, 0, 1);
+    if (ret < 0)
+        return ret;
+
+    for (i = 0; i < chip->ecc_steps; i++) {
+        memcpy(oob_ptr(chip, i), mtk_oob_ptr(chip, i), fdm->reg_size);
+        memcpy(oob_parity_ptr(chip, i), mtk_oob_parity_ptr(chip, i),
+               parity_size);
+
+        if (i == mtk_nand->bad_mark.sec)
+            mtk_nand->bad_mark.bm_swap(chip, nfc->buffer, 1);
+
+        if (buf)
+            memcpy(data_ptr(chip, buf, i), mtk_data_ptr(chip, i),
+                   chip->ecc_size);
+    }
+
+    return ret;
+}
+
+static int mtk_nfc_read_page_raw_pio_irq(struct mtk_nand_chip *chip,
+        u8 *buf, int page)
+{
+    struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
+    u32 parity_size = mtk_nand->spare_per_sector - fdm->reg_size;
+    u32 i;
+    int ret;
+
+    memset(nfc->buffer, 0xff, chip->pagesize + chip->oobsize);
+    ret = mtk_nfc_read_subpage(chip, 0, chip->pagesize, nfc->buffer,
+                               page, 1, 0, 0);
+    if (ret < 0)
+        return ret;
+
+    for (i = 0; i < chip->ecc_steps; i++) {
+        memcpy(oob_ptr(chip, i), mtk_oob_ptr(chip, i), fdm->reg_size);
+        memcpy(oob_parity_ptr(chip, i), mtk_oob_parity_ptr(chip, i),
+               parity_size);
+
+        if (i == mtk_nand->bad_mark.sec)
+            mtk_nand->bad_mark.bm_swap(chip, nfc->buffer, 1);
+
+        if (buf)
+            memcpy(data_ptr(chip, buf, i), mtk_data_ptr(chip, i),
+                   chip->ecc_size);
+    }
+
+    return ret;
+}
+
+static int mtk_nfc_gpio_init(void)
+{
+#define GPIO_MODE7  (GPIO_BASE + 0x560)
+#define GPIO_MODE8  (GPIO_BASE + 0x570)
+#define GPIO_MODE9  (GPIO_BASE + 0x580)
+#define GPIO_MODE10 (GPIO_BASE + 0x590)
+
+    writel(0x1249, GPIO_MODE7);
+    writel(0x2489, GPIO_MODE8);
+    writel(0x2492, GPIO_MODE9);
+    writel(0x12, GPIO_MODE10);
+
+    return 0;
+}
+
+#define SS_SEED_NUM 128
+static u16 ss_randomizer_seed[SS_SEED_NUM] = {
+    0x576A, 0x05E8, 0x629D, 0x45A3, 0x649C, 0x4BF0, 0x2342, 0x272E,
+    0x7358, 0x4FF3, 0x73EC, 0x5F70, 0x7A60, 0x1AD8, 0x3472, 0x3612,
+    0x224F, 0x0454, 0x030E, 0x70A5, 0x7809, 0x2521, 0x48F4, 0x5A2D,
+    0x492A, 0x043D, 0x7F61, 0x3969, 0x517A, 0x3B42, 0x769D, 0x0647,
+    0x7E2A, 0x1383, 0x49D9, 0x07B8, 0x2578, 0x4EEC, 0x4423, 0x352F,
+    0x5B22, 0x72B9, 0x367B, 0x24B6, 0x7E8E, 0x2318, 0x6BD0, 0x5519,
+    0x1783, 0x18A7, 0x7B6E, 0x7602, 0x4B7F, 0x3648, 0x2C53, 0x6B99,
+    0x0C23, 0x67CF, 0x7E0E, 0x4D8C, 0x5079, 0x209D, 0x244A, 0x747B,
+    0x350B, 0x0E4D, 0x7004, 0x6AC3, 0x7F3E, 0x21F5, 0x7A15, 0x2379,
+    0x1517, 0x1ABA, 0x4E77, 0x15A1, 0x04FA, 0x2D61, 0x253A, 0x1302,
+    0x1F63, 0x5AB3, 0x049A, 0x5AE8, 0x1CD7, 0x4A00, 0x30C8, 0x3247,
+    0x729C, 0x5034, 0x2B0E, 0x57F2, 0x00E4, 0x575B, 0x6192, 0x38F8,
+    0x2F6A, 0x0C14, 0x45FC, 0x41DF, 0x38DA, 0x7AE1, 0x7322, 0x62DF,
+    0x5E39, 0x0E64, 0x6D85, 0x5951, 0x5937, 0x6281, 0x33A1, 0x6A32,
+    0x3A5A, 0x2BAC, 0x743A, 0x5E74, 0x3B2E, 0x7EC7, 0x4FD2, 0x5D28,
+    0x751F, 0x3EF8, 0x39B1, 0x4E49, 0x746B, 0x6EF6, 0x44BE, 0x6DB7
+};
+
+static void mtk_nfc_randomizer_init(struct mtk_nand_chip *chip)
+{
+#define TRAP_RANDOM_CFG    (IO_PHYS + 0x434)
+#define TRAR_RANDOM_ENABLE (NAND_BIT(2))
+    if (readl(TRAP_RANDOM_CFG) & TRAR_RANDOM_ENABLE) {
+        chip->options |= NAND_NEED_SCRAMBLING;
+        dprintf(ALWAYS, "nand randomizer trapping on!\n");
+    } else {
+        dprintf(ALWAYS, "nand randomizer trapping off!\n");
+    };
+}
+
+static void mtk_nfc_randomizer_enable(struct mtk_nand_chip *chip, int page,
+                               enum mtk_randomizer_operation rand, int repage)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+    u32 reg = 0, index;
+
+    if (!(chip->options & NAND_NEED_SCRAMBLING))
+        return;
+
+    /* randomizer type and reseed type setup */
+    reg = nfi_readl(nfc, NFI_CNFG) | CNFG_RAND_SEL;
+    if (repage)
+        reg &= ~CNFG_RESEED_SEC_EN;
+    else
+        reg |= CNFG_RESEED_SEC_EN;
+    nfi_writel(nfc, reg, NFI_CNFG);
+
+    /* randomizer seed and type setup */
+    index = page % chip->page_per_block;
+    index &= SS_SEED_NUM - 1;
+    reg = (ss_randomizer_seed[index] & RAN_SEED_MASK) << RAND_SEED_SHIFT(rand);
+    reg |= RAND_EN(rand);
+
+    nfi_writel(nfc, reg, NFI_RANDOM_CNFG);
+}
+
+static void mtk_nfc_randomizer_disable(struct mtk_nand_chip *chip)
+{
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+
+    if (!(chip->options & NAND_NEED_SCRAMBLING))
+        return;
+
+    nfi_writel(nfc, 0, NFI_RANDOM_CNFG);
+}
+
+static inline void mtk_nfc_hw_init(struct mtk_nand_chip *chip)
+{
+    struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+    struct mtk_nfc *nfc = nand_get_controller_data(chip);
+
+    /*
+     * ACCON: access timing control register
+     * -------------------------------------
+     * 31:28: minimum required time for CS post pulling down after accessing
+     *  the device
+     * 27:22: minimum required time for CS pre pulling down before accessing
+     *  the device
+     * 21:16: minimum required time from NCEB low to NREB low
+     * 15:12: minimum required time from NWEB high to NREB low.
+     * 11:08: write enable hold time
+     * 07:04: write wait states
+     * 03:00: read wait states
+     */
+    if (!mtk_nand->acctiming)
+        nfi_writel(nfc, mtk_nand->acctiming, NFI_ACCCON);
+    else
+        nfi_writel(nfc, 0x10404011, NFI_ACCCON);
+
+    /*
+     * CNRNB: nand ready/busy register
+     * -------------------------------
+     * 7:4: timeout register for polling the NAND busy/ready signal
+     * 0  : poll the status of the busy/ready signal after [7:4]*16 cycles.
+     */
+    nfi_writew(nfc, 0xf1, NFI_CNRNB);
+    nfi_writew(nfc, PAGEFMT_8K_16K, NFI_PAGEFMT);
+
+    mtk_nfc_hw_reset(nfc);
+
+    nfi_readl(nfc, NFI_INTR_STA);
+    nfi_writel(nfc, 0, NFI_INTR_EN);
+
+    return;
+}
+
+static void mtk_nfc_set_fdm(struct mtk_nfc_fdm *fdm, struct mtk_nand_chip *nand)
+{
+    struct mtk_nfc_nand_chip *chip = to_mtk_nand(nand);
+    u32 ecc_bytes;
+
+    ecc_bytes = DIV_ROUND_UP(nand->ecc_strength * ECC_PARITY_BITS, 8);
+
+    fdm->reg_size = chip->spare_per_sector - ecc_bytes;
+    if (fdm->reg_size > NFI_FDM_MAX_SIZE)
+        fdm->reg_size = NFI_FDM_MAX_SIZE;
+
+    /* bad block mark storage */
+    fdm->ecc_size = nand->fdm_ecc_size > NFI_FDM_MAX_SIZE ? NFI_FDM_MAX_SIZE : nand->fdm_ecc_size;
+
+    nand->fdm_ecc_size = fdm->ecc_size;
+    nand->oob_free_ecc_size = nand->fdm_ecc_size * nand->ecc_steps;
+    nand->oob_free_raw_size = (fdm->reg_size - fdm->ecc_size) * nand->ecc_steps;
+
+    return;
+}
+
+static int mtk_nfc_deal_oob_ecc(struct mtk_nand_chip *nand, u8 *buf, u32 offset,
+                                u32 len, bool fill_oob)
+{
+    struct mtk_nfc_nand_chip *chip = to_mtk_nand(nand);
+    struct mtk_nfc_fdm *fdm = &chip->fdm;
+    u32 copy_len, done = 0, sector = offset / fdm->ecc_size;
+    u8 *to, *from;
+
+    while (len) {
+        offset %= fdm->ecc_size;
+        copy_len = MIN(len, fdm->ecc_size - offset);
+        if (fill_oob) {
+            to = oob_ptr(nand, sector) + offset;
+            from = buf + done;
+        } else {
+            to = buf + done;
+            from = oob_ptr(nand, sector) + offset;
+        }
+        memcpy(to, from, copy_len);
+        done += copy_len;
+        len -= copy_len;
+        sector++;
+        offset += copy_len;
+    };
+
+    return 0;
+}
+
+static int mtk_nfc_deal_oob_raw(struct mtk_nand_chip *nand, u8 *buf, u32 offset,
+                                u32 len, bool fill_oob)
+{
+    struct mtk_nfc_nand_chip *chip = to_mtk_nand(nand);
+    struct mtk_nfc_fdm *fdm = &chip->fdm;
+    u32 copy_len, done = 0, sector;
+    u32 fdm_raw_size = fdm->reg_size - fdm->ecc_size;
+    u8 *to, *from;
+
+    sector = offset / fdm_raw_size;
+    while (len) {
+        offset %= fdm_raw_size;
+        copy_len = MIN(len, fdm_raw_size - offset);
+        if (fill_oob) {
+            to = oob_ptr(nand, sector) + fdm->ecc_size + offset;
+            from = buf + done;
+        } else {
+            to = buf + done;
+            from = oob_ptr(nand, sector) + fdm->ecc_size + offset;
+        }
+        memcpy(to, from, copy_len);
+        done += copy_len;
+        len -= copy_len;
+        sector++;
+        offset += copy_len;
+    };
+
+    return 0;
+}
+
+static int mtk_nfc_deal_oob_parity(struct mtk_nand_chip *nand, u8 *buf,
+                                   u32 offset, u32 len, bool fill_oob)
+{
+    struct mtk_nfc_nand_chip *chip = to_mtk_nand(nand);
+    struct mtk_nfc_fdm *fdm = &chip->fdm;
+    u32 copy_len, done = 0, sector;
+    u32 parity_size = chip->spare_per_sector - fdm->reg_size;
+    u8 *to, *from;
+
+    sector = offset / parity_size;
+    while (len) {
+        offset %= parity_size;
+        copy_len = MIN(len, parity_size - offset);
+        if (fill_oob) {
+            to = oob_parity_ptr(nand, sector) + offset;
+            from = buf + done;
+        } else {
+            to = buf + done;
+            from = oob_parity_ptr(nand, sector) + offset;
+        }
+        memcpy(to, from, copy_len);
+        done += copy_len;
+        len -= copy_len;
+        sector++;
+        offset += copy_len;
+    };
+
+    return 0;
+}
+
+static int mtk_nfc_transfer_oob_ecc(struct mtk_nand_chip *nand, u8 *buf,
+                                    u32 offset, u32 len)
+{
+    return mtk_nfc_deal_oob_ecc(nand, buf, offset, len, false);
+}
+
+static int mtk_nfc_transfer_oob_raw(struct mtk_nand_chip *nand, u8 *buf,
+                                    u32 offset, u32 len)
+{
+    return mtk_nfc_deal_oob_raw(nand, buf, offset, len, false);
+}
+
+static int mtk_nfc_transfer_oob_parity(struct mtk_nand_chip *nand, u8 *buf,
+                                       u32 offset, u32 len)
+{
+    return mtk_nfc_deal_oob_parity(nand, buf, offset, len, false);
+}
+
+static int mtk_nfc_fill_oob_ecc(struct mtk_nand_chip *nand, u8 *buf,
+                                u32 offset, u32 len)
+{
+    return mtk_nfc_deal_oob_ecc(nand, buf, offset, len, true);
+}
+
+static int mtk_nfc_fill_oob_raw(struct mtk_nand_chip *nand, u8 *buf,
+                                u32 offset, u32 len)
+{
+    return mtk_nfc_deal_oob_raw(nand, buf, offset, len, true);
+}
+
+static int mtk_nfc_fill_oob_parity(struct mtk_nand_chip *nand, u8 *buf,
+                                   u32 offset, u32 len)
+{
+    return mtk_nfc_deal_oob_parity(nand, buf, offset, len, true);
+}
+
+static void mtk_nfc_set_bad_mark_ctl(struct mtk_nfc_bad_mark_ctl *bm_ctl,
+                                     struct mtk_nand_chip *nand)
+{
+    if (nand->pagesize == 512) {
+        bm_ctl->bm_swap = mtk_nfc_no_bad_mark_swap;
+    } else {
+        bm_ctl->bm_swap = mtk_nfc_bad_mark_swap;
+        bm_ctl->sec = nand->pagesize / mtk_data_len(nand);
+        bm_ctl->pos = nand->pagesize % mtk_data_len(nand);
+    }
+
+    return;
+}
+
+static void mtk_nfc_set_spare_per_sector(u32 *sps, struct mtk_nand_chip *nand)
+{
+    u32 spare[] = {16, 26, 27, 28, 32, 36, 40, 44,
+                   48, 49, 50, 51, 52, 62, 63, 64
+                  };
+    u32 eccsteps, i;
+
+    eccsteps = nand->pagesize / nand->ecc_size;
+    *sps = nand->oobsize / eccsteps;
+
+    if (nand->ecc_size == 1024)
+        *sps >>= 1;
+
+    for (i = 0; i < sizeof(spare) / sizeof(u32); i++) {
+        if (*sps <= spare[i]) {
+            if (*sps == spare[i])
+                *sps = spare[i];
+            else if (i != 0)
+                *sps = spare[i - 1];
+            break;
+        }
+    }
+
+    if (i >= sizeof(spare) / sizeof(u32))
+        *sps = spare[sizeof(spare) / sizeof(u32) - 1];
+
+    if (nand->ecc_size == 1024)
+        *sps <<= 1;
+
+    return;
+}
+
+int mtk_nfc_nand_chip_init(struct mtk_nand_chip **ext_nand)
+{
+    struct mtk_nfc *nfc;
+    struct mtk_nfc_nand_chip *chip;
+    struct mtk_nand_chip *nand;
+    int ret = 0;
+
+    nfc = (struct mtk_nfc *)malloc(sizeof(*nfc));
+    if (!nfc)
+        return -ENOMEM;
+    memset(nfc, 0, sizeof(*nfc));
+    nfc->regs = NFI_BASE;
+
+    chip = (struct mtk_nfc_nand_chip *)malloc(sizeof(*chip));
+    if (!chip) {
+        goto free_nfc;
+        ret = -ENOMEM;
+    }
+    memset(chip, 0, sizeof(*chip));
+
+    /* register interrupt handler */
+    mtk_nfc_request_irq(nfc);
+
+    nand = &chip->chip;
+    *ext_nand = nand;
+
+    nand_set_controller_data(nand, nfc);
+
+    nand->dev_ready = mtk_nfc_dev_ready;
+    nand->wait_busy_irq = mtk_nfc_wait_busy_irq;
+    nand->select_chip = mtk_nfc_select_chip;
+    nand->write_byte = mtk_nfc_write_byte;
+    nand->write_buf = mtk_nfc_write_buf;
+    nand->read_byte = mtk_nfc_read_byte;
+    nand->read_buf = mtk_nfc_read_buf;
+    nand->cmd_ctrl = mtk_nfc_cmd_ctrl;
+
+    nand->write_page_ecc_dma_irq = mtk_nfc_write_page_ecc_dma_irq;
+    nand->write_page_ecc_dma_polling = mtk_nfc_write_page_ecc_dma_polling;
+    nand->write_page_ecc_pio_irq = mtk_nfc_write_page_ecc_pio_irq;
+    nand->write_page_ecc_pio_polling = mtk_nfc_write_page_ecc_pio_polling;
+    nand->write_page_raw_dma_irq = mtk_nfc_write_page_raw_dma_irq;
+    nand->write_page_raw_dma_polling = mtk_nfc_write_page_raw_dma_polling;
+    nand->write_page_raw_pio_irq = mtk_nfc_write_page_raw_pio_irq;
+    nand->write_page_raw_pio_polling = mtk_nfc_write_page_raw_pio_polling;
+    nand->write_subpage_ecc_dma_irq = mtk_nfc_write_subpage_ecc_dma_irq;
+    nand->write_subpage_ecc_dma_polling = mtk_nfc_write_subpage_ecc_dma_polling;
+    nand->write_subpage_ecc_pio_irq = mtk_nfc_write_subpage_ecc_pio_irq;
+    nand->write_subpage_ecc_pio_polling = mtk_nfc_write_subpage_ecc_pio_polling;
+
+    nand->read_subpage_ecc_dma_irq = mtk_nfc_read_subpage_ecc_dma_irq;
+    nand->read_subpage_ecc_dma_polling = mtk_nfc_read_subpage_ecc_dma_polling;
+    nand->read_subpage_ecc_pio_irq = mtk_nfc_read_subpage_ecc_pio_irq;
+    nand->read_subpage_ecc_pio_polling = mtk_nfc_read_subpage_ecc_pio_polling;
+    nand->read_page_ecc_dma_irq = mtk_nfc_read_page_ecc_dma_irq;
+    nand->read_page_ecc_dma_polling = mtk_nfc_read_page_ecc_dma_polling;
+    nand->read_page_ecc_pio_irq = mtk_nfc_read_page_ecc_pio_irq;
+    nand->read_page_ecc_pio_polling = mtk_nfc_read_page_ecc_pio_polling;
+    nand->read_page_raw_dma_irq = mtk_nfc_read_page_raw_dma_irq;
+    nand->read_page_raw_dma_polling = mtk_nfc_read_page_raw_dma_polling;
+    nand->read_page_raw_pio_irq = mtk_nfc_read_page_raw_pio_irq;
+    nand->read_page_raw_pio_polling = mtk_nfc_read_page_raw_pio_polling;
+
+    nand->enable_randomizer = mtk_nfc_randomizer_enable;
+    nand->disable_randomizer = mtk_nfc_randomizer_disable;
+
+    nand->fill_oob_ecc = mtk_nfc_fill_oob_ecc;
+    nand->fill_oob_raw = mtk_nfc_fill_oob_raw;
+    nand->fill_oob_parity = mtk_nfc_fill_oob_parity;
+    nand->transfer_oob_ecc = mtk_nfc_transfer_oob_ecc;
+    nand->transfer_oob_raw = mtk_nfc_transfer_oob_raw;
+    nand->transfer_oob_parity = mtk_nfc_transfer_oob_parity;
+
+    /* default acc timing */
+    chip->acctiming = 0x10404011;
+    mtk_nfc_gpio_init();
+    mtk_nfc_randomizer_init(nand);
+    mtk_nfc_hw_init(nand);
+
+    ret = mtk_ecc_hw_init(&nfc->ecc);
+    if (ret)
+        goto free_chip;
+
+    ret = mtk_nand_scan(nand, 1 /*MTK_NAND_MAX_NSELS*/);
+    if (ret)
+        goto free_ecc;
+
+    mtk_nfc_set_spare_per_sector(&chip->spare_per_sector, nand);
+    mtk_nfc_set_fdm(&chip->fdm, nand);
+    mtk_nfc_set_bad_mark_ctl(&chip->bad_mark, nand);
+
+    nfc->buffer = (u8 *)memalign(16, nand->pagesize + nand->oobsize);
+    if (!nfc->buffer) {
+        ret = -ENOMEM;
+        goto free_databuf;
+    }
+
+    mutex_init(&nfc->lock);
+
+    ret = mtk_nand_scan_tail(nand);
+    if (ret)
+        goto free_buffer;
+
+    LTRACEF("nand chip init done\n");
+    return 0;
+
+free_buffer:
+    free(nfc->buffer);
+free_databuf:
+    free(nand->databuf);
+free_ecc:
+    free(nfc->ecc);
+free_chip:
+    free(chip);
+free_nfc:
+    free(nfc);
+
+    return ret;
+}
+
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/nand/rules.mk b/src/bsp/lk/platform/mediatek/mt2712/drivers/nand/rules.mk
new file mode 100644
index 0000000..161e76c
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/nand/rules.mk
@@ -0,0 +1,18 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS += \
+    $(LOCAL_DIR)/mtk_ecc_hal.c \
+    $(LOCAL_DIR)/mtk_nfi_hal.c \
+    $(LOCAL_DIR)/mtk_nand_bbt.c \
+    $(LOCAL_DIR)/mtk_nand_device.c \
+    $(LOCAL_DIR)/mtk_nand_nal.c \
+    $(LOCAL_DIR)/mtk_nand_nftl.c \
+    $(LOCAL_DIR)/mtk_nand_test.c \
+
+MODULE_DEPS += \
+    lib/bio \
+    lib/nftl \
+    lib/partition \
+
+include make/module.mk
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/nor/mtk_nor.c b/src/bsp/lk/platform/mediatek/mt2712/drivers/nor/mtk_nor.c
new file mode 100644
index 0000000..f81c87c
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/nor/mtk_nor.c
@@ -0,0 +1,800 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <arch/ops.h>
+#include <errno.h>
+#include <kernel/vm.h>
+#include <lib/bio.h>
+#include <lib/partition.h>
+#include <kernel/event.h>
+#include <platform/mt_reg_base.h>
+#include <platform/mtk_nor.h>
+#include <platform/interrupts.h>
+#include <string.h>
+#include <sys/types.h>
+#include <platform/mt_irq.h>
+#include <reg.h>
+#include <trace.h>
+
+#define LOCAL_TRACE 0
+
+/* nor register offset */
+#define SFLASH_MEM_BASE         ((u32)0x30000000)//mmu on
+#define SFLASH_CMD_REG          ((u32)0x00)
+#define SFLASH_CNT_REG          ((u32)0x04)
+#define SFLASH_RDSR_REG         ((u32)0x08)
+#define SFLASH_RDATA_REG        ((u32)0x0C)
+#define SFLASH_RADR0_REG        ((u32)0x10)
+#define SFLASH_RADR1_REG        ((u32)0x14)
+#define SFLASH_RADR2_REG        ((u32)0x18)
+#define SFLASH_WDATA_REG        ((u32)0x1C)
+#define SFLASH_PRGDATA0_REG     ((u32)0x20)
+#define SFLASH_PRGDATA1_REG     ((u32)0x24)
+#define SFLASH_PRGDATA2_REG     ((u32)0x28)
+#define SFLASH_PRGDATA3_REG     ((u32)0x2C)
+#define SFLASH_PRGDATA4_REG     ((u32)0x30)
+#define SFLASH_PRGDATA5_REG     ((u32)0x34)
+#define SFLASH_SHREG0_REG       ((u32)0x38)
+#define SFLASH_SHREG1_REG       ((u32)0x3C)
+#define SFLASH_SHREG2_REG       ((u32)0x40)
+#define SFLASH_SHREG3_REG       ((u32)0x44)
+#define SFLASH_SHREG4_REG       ((u32)0x48)
+#define SFLASH_SHREG5_REG       ((u32)0x4C)
+#define SFLASH_SHREG6_REG       ((u32)0x50)
+#define SFLASH_SHREG7_REG       ((u32)0x54)
+#define SFLASH_SHREG8_REG       ((u32)0x58)
+#define SFLASH_SHREG9_REG       ((u32)0x5C)
+#define SFLASH_FLHCFG_REG       ((u32)0x84)
+#define SFLASH_PP_DATA_REG      ((u32)0x98)
+#define SFLASH_PREBUF_STUS_REG  ((u32)0x9C)
+#define SFLASH_SF_INTRSTUS_REG  ((u32)0xA8)
+#define SFLASH_SF_INTREN_REG    ((u32)0xAC)
+#define SFLASH_SF_TIME_REG      ((u32)0x94)
+#define SFLASH_CHKSUM_CTL_REG   ((u32)0xB8)
+#define SFLASH_CHECKSUM_REG     ((u32)0xBC)
+#define SFLASH_CMD2_REG         ((u32)0xC0)
+#define SFLASH_WRPROT_REG       ((u32)0xC4)
+#define SFLASH_RADR3_REG        ((u32)0xC8)
+#define SFLASH_READ_DUAL_REG    ((u32)0xCC)
+#define SFLASH_DELSEL0_REG      ((u32)0xA0)
+#define SFLASH_DELSEL1_REG      ((u32)0xA4)
+#define SFLASH_DELSEL2_REG      ((u32)0xD0)
+#define SFLASH_DELSEL3_REG      ((u32)0xD4)
+#define SFLASH_DELSEL4_REG      ((u32)0xD8)
+
+#define SFLASH_CFG1_REG         ((u32)0x60)
+#define SFLASH_CFG2_REG         ((u32)0x64)
+#define SFLASH_CFG3_REG         ((u32)0x68)
+#define SFLASH_STATUS0_REG      ((u32)0x70)
+#define SFLASH_STATUS1_REG      ((u32)0x74)
+#define SFLASH_STATUS2_REG      ((u32)0x78)
+#define SFLASH_STATUS3_REG      ((u32)0x7C)
+
+#define SFLASH_WRBUF_SIZE           128
+#define SFLASH_POLLINGREG_COUNT     500000
+#define SFLASH_WRITEBUSY_TIMEOUT    500000
+#define SFLASH_ERASESECTOR_TIMEOUT  500000
+#define SFLASH_HW_ALIGNMENT         4
+
+/* nor register read/write */
+#define IO_READ32(base, offset)           readl(base + offset)
+#define IO_WRITE32(offset, value)         writel(value, offset)
+
+#define SFLASH_WREG8(offset, value)       writeb(value, NOR_BASE + offset)
+#define SFLASH_RREG8(offset)              readb(NOR_BASE + offset)
+#define SFLASH_WREG32(offset, value)      writel(value, NOR_BASE + offset)
+#define SFLASH_RREG32(offset)             readl(NOR_BASE + offset)
+
+#define SFLASH_DMA_REG_BASE         (NOR_BASE + 0x718)
+#define SFLASH_DMA_WREG32(offset, value) \
+    (*(volatile unsigned int *)(SFLASH_DMA_REG_BASE + offset)) = (value)
+#define SFLASH_DMA_RREG32(offset) \
+    (*(volatile unsigned int *)(SFLASH_DMA_REG_BASE + offset))
+#define SFLASH_DMA_ALIGN    0x10U
+
+#define LoWord(d)     ((uint16_t)(d & 0x0000ffffL))
+#define HiWord(d)     ((uint16_t)((d >> 16) & 0x0000ffffL))
+#define LoByte(w)     ((uint8_t)(w & 0x00ff))
+#define HiByte(w)     ((uint8_t)((w >> 8) & 0x00ff))
+
+#define NOR_TEST      0
+#define CMD_4KB       0x20
+#define CMD_64KB      0xD8
+#define BLK_SIZE      512
+
+typedef struct {
+    u8   u1MenuID;
+    u8   u1DevID1;
+    u8   u1DevID2;
+    u32  u4ChipSize;
+    u32  u4SecSize;
+    u8   u1WRENCmd;
+} SFLASHHW_VENDOR_T;
+
+static SFLASHHW_VENDOR_T *flash_desc;
+static SFLASHHW_VENDOR_T vendor_desc[] = {
+    {0x20, 0xBA, 0x19, 0x02000000, 0x10000, 0x06}, /* ST(M25Q256) */
+    {0xC2, 0x20, 0x13, 0x00080000, 0x10000, 0x06}, /* MXIC(25L400) */
+    {0xC2, 0x20, 0x14, 0x00100000, 0x10000, 0x06}, /* MXIC(25L80) */
+    {0xC2, 0x20, 0x15, 0x00200000, 0x10000, 0x06}, /* MXIC(25L160) */
+    {0xC2, 0x24, 0x15, 0x00200000, 0x10000, 0x06}, /* MXIC(25L1635D) */
+    {0xC2, 0x20, 0x16, 0x00400000, 0x10000, 0x06}, /* MXIC(25L320) */
+    {0xC2, 0x20, 0x17, 0x00800000, 0x10000, 0x06}, /* MXIC(25L640) */
+    {0xC2, 0x20, 0x18, 0x01000000, 0x10000, 0x06}, /* MXIC(25L1280) */
+    {0xC2, 0x5E, 0x16, 0x00400000, 0x10000, 0x06}, /* MXIC(25L3235D) */
+    {0xC2, 0x20, 0x19, 0x02000000, 0x10000, 0x06}, /* MXIC(25L256) */
+    {0xC2, 0x20, 0x1A, 0x02000000, 0x10000, 0x06}, /* MXIC(25L512) */
+    {0xC2, 0x25, 0x39, 0x02000000, 0x10000, 0x06}, /* MXIC(25U256) */
+    {0xEF, 0x30, 0x13, 0x00080000, 0x10000, 0x06}, /* WINBOND(W25X40) */
+    {0xEF, 0x30, 0x14, 0x00100000, 0x10000, 0x06}, /* WINBOND(W25X80) */
+    {0xEF, 0x30, 0x15, 0x00200000, 0x10000, 0x06}, /* WINBOND(W25X16) */
+    {0xEF, 0x30, 0x16, 0x00400000, 0x10000, 0x06}, /* WINBOND(W25X32) */
+    {0xEF, 0x60, 0x16, 0x00400000, 0x10000, 0x06}, /* WINBOND(W25X32) */
+    {0xEF, 0x30, 0x17, 0x00800000, 0x10000, 0x06}, /* WINBOND(W25X64) */
+    {0xEF, 0x40, 0x19, 0x02000000, 0x10000, 0x06}, /* WINBOND(W25Q256FV) */
+    {0xEF, 0x40, 0x15, 0x00200000, 0x10000, 0x06}, /* WINBOND(W25Q16CV) */
+    {0xEF, 0x40, 0x16, 0x00400000, 0x10000, 0x06}, /* WINBOND(W25Q32BV) */
+    {0xEF, 0x40, 0x17, 0x00800000, 0x10000, 0x06}, /* WINBOND(W25Q64BV) */
+    {0xEF, 0x40, 0x18, 0x01000000, 0x10000, 0x06}, /* WINBOND(W25Q128BV) */
+    {0x00, 0x00, 0x00, 0x00000000, 0x00000, 0x00}, /* NULL Device */
+};
+static event_t nor_int_event;
+
+static void SF_NOR_GPIO_INIT(void)
+{
+    /* use pinmux default state. */
+}
+
+static int _PollingReg(u8 u1RegOffset, u8 u8Compare)
+{
+    u32 u4Polling;
+    u8 u1Reg;
+
+    u4Polling = 0;
+    while (1) {
+        u1Reg = SFLASH_RREG8(u1RegOffset);
+        if (0x00 == (u1Reg & u8Compare))
+            break;
+
+        u4Polling++;
+        if (u4Polling > SFLASH_POLLINGREG_COUNT)
+            return 1;
+    }
+    return 0;
+}
+
+static int SFLASH_UnLock(void)
+{
+    SFLASH_WREG32(SFLASH_WRPROT_REG, 0x30);
+    return SFLASH_RREG32(SFLASH_WRPROT_REG);
+}
+
+static inline void _SendFlashCommand(u8 u1Val)
+{
+    SFLASH_WREG8(SFLASH_CMD_REG, u1Val);
+}
+
+static int _SetFlashWriteEnable(void)
+{
+    SFLASH_WREG8(SFLASH_PRGDATA5_REG, flash_desc->u1WRENCmd);
+    SFLASH_WREG8(SFLASH_CNT_REG,8);
+
+    _SendFlashCommand(0x4);
+
+    return _PollingReg(SFLASH_CMD_REG, 0x04);
+}
+
+static int SFLASHHW_ReadFlashStatus(u8 *pu1Val)
+{
+    if (pu1Val == NULL)
+        return 1;
+
+    _SendFlashCommand(0x2);
+
+    if (_PollingReg(SFLASH_CMD_REG, 0x2) != 0)
+        return 1;
+
+    *pu1Val = SFLASH_RREG8(SFLASH_RDSR_REG);
+    return 0;
+}
+
+static int _WaitForWriteBusy(u32 u4Timeout)
+{
+    u32 u4Count;
+    u8 u1Reg;
+
+    u4Count = 0;
+    while (1) {
+        if (SFLASHHW_ReadFlashStatus(&u1Reg) != 0)
+            return 1;
+
+        if (0 == (u1Reg & 0x1))
+            break;
+
+        u4Count++;
+        if (u4Count > u4Timeout)
+            return 1;
+    }
+    return 0;
+}
+
+static int _WBEnable(void)
+{
+    u32 u4Polling;
+    u8 u1Reg;
+    u8 u1Tmp = 0x01;
+
+    SFLASH_WREG8(SFLASH_CFG2_REG, u1Tmp);
+
+    u4Polling = 0;
+    while (1) {
+        u1Reg = SFLASH_RREG8(SFLASH_CFG2_REG);
+        if (0x01 == (u1Reg & 0x1))
+            break;
+
+        u4Polling++;
+        if (u4Polling > SFLASH_POLLINGREG_COUNT)
+            return 1;
+    }
+    return 0;
+}
+
+static int _WBDisable(void)
+{
+    u32 u4Polling;
+    u8 u1Reg;
+    u8 u1Tmp = 0x00;
+
+    SFLASH_WREG8(SFLASH_CFG2_REG, u1Tmp);
+    u4Polling = 0;
+    while (1) {
+        u1Reg = SFLASH_RREG8(SFLASH_CFG2_REG);
+        if (u1Tmp == (u1Reg & 0xF))
+            break;
+
+        u4Polling++;
+        if (u4Polling > SFLASH_POLLINGREG_COUNT)
+            return 1;
+    }
+    return 0;
+}
+
+static int _ExecuteWriteCmd(void)
+{
+    u8 u1Reg;
+
+    _SendFlashCommand(0x10);
+    while (1) {
+        u1Reg = SFLASH_RREG8(SFLASH_CMD_REG);
+        if (0x0 == (u1Reg & 0x10))
+            break;
+    }
+    return 0;
+}
+
+static int SFLASHHW_WriteProtect(bool fgEnable)
+{
+    if (_WaitForWriteBusy(SFLASH_WRITEBUSY_TIMEOUT) != 0)
+        return -1;
+
+    if (_SetFlashWriteEnable() != 0)
+        return -1;
+    if (fgEnable)
+        SFLASH_WREG8(SFLASH_PRGDATA5_REG, 0x0);
+    else
+        SFLASH_WREG32(SFLASH_PRGDATA5_REG, 0x0);
+
+    SFLASH_WREG8(SFLASH_CNT_REG,8);
+    _SendFlashCommand(0x20);
+
+    if (_PollingReg(SFLASH_CMD_REG, 0x20) != 0)
+        return -1;
+    return _WaitForWriteBusy(SFLASH_WRITEBUSY_TIMEOUT);
+}
+
+static int enter_4byte(void)
+{
+    u8 val;
+
+    if (_SetFlashWriteEnable() != 0) {
+        LTRACEF("_SetFlashWriteEnable fail!\n");
+        return 1;
+    }
+
+    SFLASH_WREG8(SFLASH_PRGDATA5_REG, 0xb7);
+    SFLASH_WREG8(SFLASH_CNT_REG, 8);
+    _SendFlashCommand(0x4);
+    if (_PollingReg(SFLASH_CMD_REG, 0x4) != 0)
+        return -1;
+
+    if (_WaitForWriteBusy(SFLASH_WRITEBUSY_TIMEOUT))
+        return -1;
+
+    val = SFLASH_RREG8(SFLASH_READ_DUAL_REG);
+    SFLASH_WREG8(SFLASH_READ_DUAL_REG, val | 0x10);
+
+    return 0;
+}
+
+static int SFLASHHW_GetID(u8 *pu1MenuID, u8 *pu1DevID1, u8 *pu1DevID2)
+{
+    SFLASH_WREG8(SFLASH_PRGDATA5_REG, 0x9F);
+    SFLASH_WREG8(SFLASH_PRGDATA4_REG, 0x00);
+    SFLASH_WREG8(SFLASH_PRGDATA3_REG, 0x00);
+    SFLASH_WREG8(SFLASH_PRGDATA2_REG, 0x00);
+    SFLASH_WREG8(SFLASH_CNT_REG, 32);
+
+    _SendFlashCommand(0x4);
+
+    if ( _PollingReg(SFLASH_CMD_REG, 0x04) !=0)
+        return 1;
+
+    *pu1DevID2 = SFLASH_RREG8(SFLASH_SHREG0_REG);
+    *pu1DevID1 = SFLASH_RREG8(SFLASH_SHREG1_REG);
+    *pu1MenuID = SFLASH_RREG8(SFLASH_SHREG2_REG);
+
+    _SendFlashCommand(0x0);
+    if ((pu1MenuID != NULL) && (pu1DevID1 != NULL)) {
+        if ((*pu1MenuID == 0xFF) || (*pu1DevID1 == 0x00))
+            return 1;
+    }
+    return 0;
+}
+
+static int _DoIdentify(u32 *pu4Vendor)
+{
+    u32 i;
+    u8 menuID, devID1, devID2;
+
+    if (pu4Vendor == NULL)
+        return 1;
+    *pu4Vendor = 0xFFFFFFFF;
+    if (SFLASHHW_GetID(&menuID, &devID1, &devID2) != 0)
+        return 1;
+
+    dprintf(CRITICAL,"MenuID: 0x%X, DeviceID1: 0x%X, DeviceID2: 0x%X\n",
+            menuID, devID1, devID2);
+    i = 0;
+
+    while (vendor_desc[i].u1MenuID != (u8)0x0) {
+        if ( (vendor_desc[i].u1MenuID == menuID) &&
+                (vendor_desc[i].u1DevID1 == devID1) &&
+                (vendor_desc[i].u1DevID2 == devID2)) {
+            *pu4Vendor = i;
+            flash_desc = &vendor_desc[i];
+            return 0;
+        }
+        i++;
+    }
+    return 1;
+}
+
+static int SFLASHHW_Init(void)
+{
+    u32 u4VendorIdx;
+    u32 _u4ChipCount = 0;
+
+    SFLASH_WREG32(SFLASH_WRPROT_REG, 0x30);
+    SF_NOR_GPIO_INIT();
+
+    if (_DoIdentify(&u4VendorIdx) != 0)
+        return 1;
+
+    dprintf(CRITICAL, "Detect flash #%d\n", u4VendorIdx);
+    _u4ChipCount++;
+
+    if (_u4ChipCount == 0) {
+        dprintf(CRITICAL, "There is no flash!\n");
+        return 1;
+    }
+
+    SFLASH_WREG8(SFLASH_READ_DUAL_REG, 0);
+    /* Clear interrupts */
+    SFLASH_WREG8(SFLASH_SF_INTREN_REG, 0x0);
+    SFLASH_WREG8(SFLASH_SF_INTRSTUS_REG, 0xff);
+
+    /* chipsize > 16MB, enter 4Byte address mode. */
+    if (flash_desc->u4ChipSize > 0x1000000) {
+        if (enter_4byte())
+            return 1;
+    }
+
+    return 0;
+}
+
+static enum handler_return nor_interrupt_handler(void *arg)
+{
+    /* Clear event bits */
+    SFLASH_WREG8(SFLASH_SF_INTREN_REG, 0x0);
+    SFLASH_WREG8(SFLASH_SF_INTRSTUS_REG, SFLASH_RREG8(SFLASH_SF_INTRSTUS_REG));
+    /* MUST BE *false*! otherwise, schedule in intr routine */
+    event_signal(&nor_int_event, false);
+
+    return INT_RESCHEDULE;
+}
+
+static int NOR_Init(void)
+{
+    SFLASH_UnLock();
+    if (SFLASHHW_Init() != 0)
+        return -1;
+
+    /* Register msdc irq */
+    mt_irq_set_sens(MT_NOR_IRQ_ID, LEVEL_SENSITIVE);
+    mt_irq_set_polarity(MT_NOR_IRQ_ID, MT65xx_POLARITY_LOW);
+    event_init(&nor_int_event, false, EVENT_FLAG_AUTOUNSIGNAL);
+    register_int_handler(MT_NOR_IRQ_ID, nor_interrupt_handler, NULL);
+    unmask_interrupt(MT_NOR_IRQ_ID);
+
+    return 0;
+}
+
+static int _WriteBuffer(u32 u4Addr, u32 u4Len, const u8 *pu1Buf)
+{
+    u32 i, j, u4BufIdx, u4Data;
+
+    if (pu1Buf == NULL)
+        return 1;
+
+    ASSERT(u4Len <= SFLASH_WRBUF_SIZE);
+    ASSERT((u4Addr%SFLASH_HW_ALIGNMENT) == 0);
+    ASSERT((u4Len%SFLASH_HW_ALIGNMENT) == 0);
+
+    SFLASH_WREG8(SFLASH_RADR3_REG, HiByte(HiWord(u4Addr)));
+    SFLASH_WREG8(SFLASH_RADR2_REG, LoByte(HiWord(u4Addr)));
+    SFLASH_WREG8(SFLASH_RADR1_REG, HiByte(LoWord(u4Addr)));
+    SFLASH_WREG8(SFLASH_RADR0_REG, LoByte(LoWord(u4Addr)));
+
+    u4BufIdx = 0;
+    for (i=0; i<u4Len; i+=4) {
+        for (j=0; j<4; j++) {
+            (*((u8 *)&u4Data + j)) = pu1Buf[u4BufIdx];
+            u4BufIdx++;
+        }
+        SFLASH_WREG32(SFLASH_PP_DATA_REG, u4Data);
+    }
+
+    if (_ExecuteWriteCmd() != 0)
+        return 1;
+
+    if (_WaitForWriteBusy(SFLASH_WRITEBUSY_TIMEOUT) != 0)
+        return 1;
+    return 0;
+}
+
+static int SFLASHHW_WriteSector(u32 u4Addr, u32 u4Len, const u8 *pu1Buf)
+{
+    u32 u4Count;
+
+    if (_SetFlashWriteEnable() != 0)
+        return -EIO;
+
+    if (_WBEnable() != 0)
+        return -EIO;
+
+    while ((int)u4Len > 0) {
+        if (u4Len >= SFLASH_WRBUF_SIZE)
+            u4Count = SFLASH_WRBUF_SIZE;
+        else
+            break;
+
+        if (_WriteBuffer(u4Addr, u4Count, pu1Buf) != 0) {
+            dprintf(CRITICAL, "Write err! addr: 0x%x\n", u4Addr);
+            if (_WBDisable() != 0)
+                return -EIO;
+            return -EIO;
+        }
+        u4Len -= u4Count;
+        u4Addr += u4Count;
+        pu1Buf += u4Count;
+    }
+    if (_WBDisable() != 0)
+        return -EIO;
+
+    return 0;
+}
+
+static ssize_t dma_read(uint32_t u4SrcAddr, unsigned long u4DestAddr, uint32_t u4Size)
+{
+    int ret;
+    uint32_t dmaAddr;
+
+#if WITH_KERNEL_VM
+    dmaAddr = (uint32_t)kvaddr_to_paddr((void *)u4DestAddr);
+#else
+    dmaAddr = (uint32_t)u4DestAddr;
+#endif
+
+    arch_clean_invalidate_cache_range(u4DestAddr, u4Size);
+
+    SFLASH_WREG8(SFLASH_SF_INTREN_REG, 0x80);
+    SFLASH_WREG8(SFLASH_SF_INTRSTUS_REG, 0xff);
+
+    /* Do reset */
+    SFLASH_DMA_WREG32(0x0, 0x0);
+    SFLASH_DMA_WREG32(0x0, 0x2);
+    SFLASH_DMA_WREG32(0x0, 0x4);
+
+    /* Flash source address and DRAM destination address */
+    SFLASH_DMA_WREG32(0x4, u4SrcAddr);
+    SFLASH_DMA_WREG32(0x8, dmaAddr);
+    SFLASH_DMA_WREG32(0xC, dmaAddr + u4Size);
+
+    /* Trigger DMA */
+    SFLASH_DMA_WREG32(0x0, 0x5);
+
+    /* Wait for interrupt */
+    ret = event_wait_timeout(&nor_int_event, 10000);
+    if (ret != 0) {
+        dprintf(CRITICAL, "%s -> wait interrupt timeout\n", __func__);
+        /* Clear IntrEn */
+        SFLASH_WREG8(SFLASH_SF_INTREN_REG, 0x0);
+        return ret;
+    }
+
+    return u4Size;
+}
+
+static int SFLASHHW_EraseSector(u32 u4Addr, u32 command)
+{
+    u32 u4Polling;
+    u8 u1Reg;
+
+    if (SFLASHHW_WriteProtect(false) != 0) {
+        LTRACEF("Disable Flash write protect fail!\n");
+        return -1;
+    }
+    if (_WaitForWriteBusy(SFLASH_WRITEBUSY_TIMEOUT) != 0) {
+        return 1;
+        LTRACEF("_WaitForWriteBusy fail!\n");
+    }
+
+    if (_SetFlashWriteEnable() != 0) {
+        LTRACEF("_SetFlashWriteEnable fail!\n");
+        return 1;
+    }
+    /* Execute sector erase command */
+    SFLASH_WREG8(SFLASH_PRGDATA5_REG, command);
+    if (SFLASH_RREG8(SFLASH_READ_DUAL_REG) & 0x10) {
+        SFLASH_WREG8(SFLASH_PRGDATA4_REG, HiByte(HiWord(u4Addr)));
+        SFLASH_WREG8(SFLASH_PRGDATA3_REG, LoByte(HiWord(u4Addr)));
+        SFLASH_WREG8(SFLASH_PRGDATA2_REG, HiByte(LoWord(u4Addr)));
+        SFLASH_WREG8(SFLASH_PRGDATA1_REG, LoByte(LoWord(u4Addr)));
+        SFLASH_WREG8(SFLASH_CNT_REG, 40);
+    } else {
+        SFLASH_WREG8(SFLASH_PRGDATA4_REG, LoByte(HiWord(u4Addr)));
+        SFLASH_WREG8(SFLASH_PRGDATA3_REG, HiByte(LoWord(u4Addr)));
+        SFLASH_WREG8(SFLASH_PRGDATA2_REG, LoByte(LoWord(u4Addr)));
+        SFLASH_WREG8(SFLASH_CNT_REG, 32);
+    }
+
+    _SendFlashCommand(0x4);
+    u4Polling = 0;
+    while (1) {
+        if (SFLASHHW_ReadFlashStatus(&u1Reg) != 0) {
+            LTRACEF("SFLASHHW_ReadFlashStatus fail!\n");
+            return 1;
+        }
+
+        if (0 == (u1Reg & 0x1))
+            break;
+
+        u4Polling++;
+        if (u4Polling > SFLASH_ERASESECTOR_TIMEOUT) {
+            LTRACEF("SFLASH_ERASESECTOR_TIMEOUT timeout!\n");
+            return 1;
+        }
+    }
+    if (SFLASHHW_WriteProtect(true) != 0) {
+        LTRACEF("Disable Flash write protect fail!\n");
+        return -1;
+    }
+    return 0;
+}
+
+static ssize_t nor_erase(struct bdev *bdev, off_t offset, size_t len)
+{
+    u32 i = 0;
+    u32 block_number = 0;
+    u32 addr = (u32)offset;
+    u32 block_size = 0;
+    u32 command = 0;
+
+    LTRACEF("addr %d, blksz %u\n", addr, flash_desc->u4SecSize);
+    if (!addr || addr > flash_desc->u4SecSize) {
+        block_size = flash_desc->u4SecSize;
+        command = CMD_64KB;
+    } else {
+        block_size = 4*1024;
+        command = CMD_4KB;
+    }
+
+    LTRACEF("nor erase block size %d\n", block_size);
+    if ((len % bdev->block_size) != 0) {
+        dprintf(ALWAYS,"nor erase fail: length must align block size!\n");
+        return -1;
+    }
+
+    block_number = len / block_size;
+    for (i = 0; i < block_number; i++) {
+        LTRACEF("erase offset from %d to %d\n", addr + i * block_size,
+                addr + (i + 1) * block_size);
+        if (SFLASHHW_EraseSector(addr + i * block_size, command))
+            return -1;
+    }
+
+    /* erase redundant block */
+    LTRACEF("erase redundant from %lu to %lu\n", addr + len - block_size,
+            addr + len);
+    if (SFLASHHW_EraseSector(addr + len - block_size, command))
+        return -1;
+
+    return len;
+}
+
+static ssize_t nor_read_block(struct bdev *bdev, void *buf, bnum_t blknr, uint blks)
+{
+    u32 offset, len;
+
+    if (((unsigned long)buf & (SFLASH_DMA_ALIGN - 1))!=0) {
+        dprintf(ALWAYS, "Address or size is not 16-byte alignment!\n");
+        return -EINVAL;
+    }
+
+    offset = blknr * bdev->block_size;
+    len = (u32)blks * bdev->block_size;
+
+    return dma_read(offset, (unsigned long)buf, len);
+}
+
+static ssize_t nor_write_block(struct bdev *bdev, const void *buf, bnum_t blknr, uint blks)
+{
+    int ret;
+    u32 addr, len;
+
+    ret = SFLASHHW_WriteProtect(false);
+    if (ret != 0) {
+        dprintf(CRITICAL, "Disable Flash write protect fail!\n");
+        return -EIO;
+    }
+
+    addr = blknr * bdev->block_size;
+    len = (u32)blks * bdev->block_size;
+
+    LTRACEF("nor write from %d to %d\n", addr, addr + len);
+    ret = SFLASHHW_WriteSector(addr, len, buf);
+    if (ret) {
+        dprintf(CRITICAL, "Write flash error ! (%d)\n", ret);
+        SFLASHHW_WriteProtect(true);
+        return ret;
+    }
+
+    ret = SFLASHHW_WriteProtect(true);
+    if (ret != 0) {
+        dprintf(CRITICAL, "Enable flash write protect fail!\n");
+        return -EIO;
+    }
+
+    return len;
+}
+
+#if NOR_TEST
+#define NOR_WRITE_OFFSET 0x700000
+#define NOR_TEST_SIZE 4096
+static void nor_test(struct bdev *dev)
+{
+    int i, ret, check_flag = 0;
+    u8 *w_buf = malloc(NOR_TEST_SIZE);
+
+    /* erase test */
+    dprintf(ALWAYS, "nor driver ut test START\n");
+    if (!SFLASHHW_EraseSector(NOR_WRITE_OFFSET, CMD_64KB))
+        dprintf(ALWAYS, "nor erase success\n");
+    else
+        dprintf(ALWAYS, "nor erase failed\n");
+
+    spin(100000);
+
+    memset(w_buf, 0x0, NOR_TEST_SIZE);
+    ret = bio_read(dev, (void *)w_buf, (off_t)NOR_WRITE_OFFSET, NOR_TEST_SIZE);
+    if (ret == NOR_TEST_SIZE)
+        dprintf(ALWAYS, "nor read success after erase\n");
+    else
+        dprintf(ALWAYS, "nor read failed after erase with ret: %d @line: %d\n",
+                ret, __LINE__);
+
+    for (i = 0; i < NOR_TEST_SIZE; i++) {
+        if (w_buf[i] != 0xff)
+            check_flag = 1;
+        dprintf(ALWAYS, " 0x%x",w_buf[i]);
+    }
+    LTRACEF("\n");
+    if (check_flag == 1) {
+        dprintf(ALWAYS, "ERROR... data not expected, fail @line: %d\n",
+                __LINE__);
+        check_flag = 0;
+    }
+
+    /* write test */
+    for (i = 0; i < NOR_TEST_SIZE; i++)
+        w_buf[i] = i & 0xFF;
+
+    ret = bio_write(dev, (void *)w_buf, (off_t)NOR_WRITE_OFFSET, NOR_TEST_SIZE);
+    if (ret == NOR_TEST_SIZE)
+        dprintf(ALWAYS, "nor write success\n");
+    else
+        dprintf(ALWAYS, "nor write failed with ret: %d @line: %d\n",
+                ret, __LINE__);
+
+    memset(w_buf, 0x0, NOR_TEST_SIZE);
+    ret = bio_read(dev, (void *)w_buf, (off_t)NOR_WRITE_OFFSET, NOR_TEST_SIZE);
+    if (ret == NOR_TEST_SIZE)
+        dprintf(ALWAYS, "nor read success after write\n");
+    else
+        dprintf(ALWAYS, "nor read failed after write with ret: %d @line: %d\n",
+                ret, __LINE__);
+
+    for (i = 0; i < NOR_TEST_SIZE; i++) {
+        if ((i & 0xFF) != w_buf[i])
+            check_flag =1;
+        dprintf(ALWAYS, " 0x%x", w_buf[i]);
+    }
+    LTRACEF("\n");
+    if (check_flag == 1)
+        dprintf(ALWAYS, "ERROR... data not expected, fail @line: %d\n",
+                __LINE__);
+
+    dprintf(ALWAYS, "nor driver ut test DONE.\n");
+    free(w_buf);
+}
+#endif
+
+void nor_init_device(void)
+{
+    struct bdev *dev;
+
+    if (NOR_Init())
+        ASSERT(0);
+
+    dev = malloc(sizeof(struct bdev));
+    ASSERT(dev);
+    memset(dev, 0, sizeof(struct bdev));
+
+    /* construct the block device */
+    bio_initialize_bdev(dev, "nor0",
+                        BLK_SIZE, flash_desc->u4ChipSize / BLK_SIZE, 0, NULL,
+                        (BIO_FLAG_CACHE_ALIGNED_READS));
+
+    /* override our block device hooks */
+    dev->read_block = nor_read_block;
+    dev->write_block = nor_write_block;
+    dev->erase = nor_erase;
+
+    bio_register_device(dev);
+    partition_publish(dev->name, 0x0);
+
+#if NOR_TEST
+    nor_test(dev);
+#endif
+}
+
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/pll/pll.c b/src/bsp/lk/platform/mediatek/mt2712/drivers/pll/pll.c
new file mode 100644
index 0000000..3f84517
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/pll/pll.c
@@ -0,0 +1,696 @@
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <debug.h>
+#include <platform/mtk_devinfo.h>
+#include <platform/pll.h>
+#include <platform/reg_utils.h>
+#include <platform/spm_mtcmos.h>
+
+#define ALL_CLK_ON  1
+#define FMETER_EN   0
+
+#define udelay(x)       spin(x)
+#define mdelay(x)       udelay((x) * 1000)
+
+#define ARRAY_SIZE(a)       (sizeof(a) / sizeof(a[0]))
+
+#if FMETER_EN
+static u32 mt_fmeter_abist(u32 id, u32 arm_k1, u32 abist_k1)
+{
+    int output = 0;
+    int i = 0;
+    u32 temp, clk26cali_0, clk_cfg_m0, clk_misc_cfg_1;
+
+    /* use AD_PLLGP_TST_CK_CKSYS to measure CVBSPLL */
+    if (id == 43) {
+        setbits32(PLL_TEST_CON0, 0x30F); /* [9:8]:TST_SEL, [3:0]:TSTOD_EN, A2DCK_EN, TSTCK_EN, TST_EN */
+        setbits32(CVBSREFPLL_CON1, 0x11); /* [4]:CVBS_MONCK_EN, [3:0]:CVBSREFPLL_TESTMUX */
+        setbits32(CVBSPLL_CON1, 0x20); /* [5]: CVBSPLL_MONCK_EN */
+    }
+
+    clk26cali_0 = readl(CLK26CALI_0);
+    writel_r(CLK26CALI_0, clk26cali_0 | 0x80); /* enable fmeter_en */
+
+    clk_misc_cfg_1 = readl(CLK_MISC_CFG_1);
+    writel_r(CLK_MISC_CFG_1, 0xFFFF0000 | (arm_k1 << 8) | abist_k1);
+    /* select divider */
+
+    clk_cfg_m0 = readl(CLK_CFG_M0);
+    writel_r(CLK_CFG_M0, (id << 8)); /* select abist_cksw */
+
+    setbits32(CLK26CALI_0, 0x1); /* start fmeter */
+
+    /* wait frequency meter finish */
+    while ((readl(CLK26CALI_0) & 0x1) && (++i <= 10))
+        udelay(50);
+
+    temp = readl(CLK26CALI_1) & 0xFFFF;
+
+    output = (temp * 26000) * (abist_k1 + 1) * (arm_k1 + 1) / 1024;
+
+    writel_r(CLK_CFG_M0, clk_cfg_m0);
+    writel_r(CLK_MISC_CFG_1, clk_misc_cfg_1);
+    writel_r(CLK26CALI_0, clk26cali_0);
+
+    if (id == 43) {
+        clrbits32(PLL_TEST_CON0, 0x30F);
+        clrbits32(CVBSREFPLL_CON1, 0x11);
+        clrbits32(CVBSPLL_CON1, 0x20);
+    }
+
+    if (i > 10)
+        return 0;
+    else
+        return output;
+}
+
+static u32 mt_fmeter_ckgen(u32 id, u32 ckgen_k1)
+{
+    int output = 0;
+    int i = 0;
+    u32 temp, clk26cali_0, clk_cfg_m1, clk_misc_cfg_1;
+
+    clk26cali_0 = readl(CLK26CALI_0);
+    writel_r(CLK26CALI_0, clk26cali_0 | 0x80); /* enable fmeter_en */
+
+    clk_misc_cfg_1 = readl(CLK_MISC_CFG_1);
+    writel_r(CLK_MISC_CFG_1, 0x00FFFFFF | ckgen_k1 << 24);
+    /* select divider */
+
+    clk_cfg_m1 = readl(CLK_CFG_M1);
+    writel_r(CLK_CFG_M1, (id << 16)); /* select ckgen_cksw */
+
+    setbits32(CLK26CALI_0, 0x10); /* start fmeter */
+
+    /* wait frequency meter finish */
+    while ((readl(CLK26CALI_0) & 0x10) && (++i <= 10))
+        udelay(50);
+
+    temp = readl(CLK26CALI_2) & 0xFFFF;
+
+    output = (temp * 26000) * (ckgen_k1 + 1) / 1024;
+
+    writel_r(CLK_CFG_M1, clk_cfg_m1);
+    writel_r(CLK_MISC_CFG_1, clk_misc_cfg_1);
+    writel_r(CLK26CALI_0, clk26cali_0);
+
+    if (i > 10)
+        return 0;
+    else
+        return output;
+}
+
+static u32 mt_get_cpu_freq(void)
+{
+    return mt_fmeter_abist(59, 0, 0);
+}
+
+static u32 mt_get_bus_freq(void)
+{
+    return mt_fmeter_ckgen(1, 0);
+}
+
+static const char *abist_clk[] = {
+    //[1] = "AD_MEMPLL2_CKOUT0_PRE_ISO(0)",
+    [2] =   "AD_ARMCA35PLL_600M_CORE_CK",
+    [3] =   "AD_ARMCA35PLL_400M_CORE_CK",
+    [4] =   "AD_MAIN_H546M_CK",
+    [5] =   "AD_MAIN_H364M_CK",
+    [6] =   "AD_MAIN_H218P4M_CK",
+    [7] =   "AD_MAIN_H156M_CK",
+    [8] =   "AD_UNIV_178P3M_CK",
+    [9] =   "AD_UNIVPLL_UNIV_48M_CK",
+    [10] =  "AD_UNIV_624M_CK",
+    [11] =  "AD_UNIV_416M_CK",
+    [12] =  "AD_UNIV_249P6M_CK",
+    [13] =  "AD_APLL1_CK",
+    [14] =  "AD_APLL2_CK",
+    [15] =  "AD_LTEPLL_FS26M_CK",
+    [16] =  "rtc32k_ck_i",
+    [17] =  "AD_MMPLL_500M_CK",
+    [18] =  "AD_VENCPLL_380M_CK",
+    [19] =  "AD_VCODEPLL_442M_CK",
+    [20] =  "AD_TVDPLL_572M_CK",
+    [21] =  "AD_LVDSPLL_150M_CK",
+    [22] =  "AD_MSDCPLL_400M_CK",
+    [23] =  "AD_ETHERPLL_50M_CK",
+    [24] =  "clkph_MCK_o",
+    [25] =  "AD_USB_48M_CK",
+    [26] =  "AD_MSDCPLL2_400M_CK",
+    [27] =  "AD_CVBSADC_CKOUTA",
+    //[28] =    "NA",
+    //[29] =    "NA",
+    [30] =  "AD_TVDPLL_429M_CK",
+    //[31] =    "soc_rosc(0)",
+    //[32] =    "soc_rosc1(0)",
+    [33] =  "AD_LVDSPLL2_150M_CK",
+    [34] =  "AD_ETHERPLL_125M_CK",
+    [35] =  "AD_MIPI_26M_CK_CKSYS",
+    [36] =  "AD_LTEPLL_ARMPLL26M_CK_CKSYS",
+    [37] =  "AD_LETPLL_SSUSB26M_CK_CKSYS",
+    [38] =  "AD_DSI2_LNTC_DSICLK_CKSYS",
+    [39] =  "AD_DSI3_LNTC_DSICLK_CKSYS",
+    [40] =  "AD_DSI2_MPPLL_TST_CK_CKSYS",
+    [41] =  "AD_DSI3_MPPLL_TST_CK_CKSYS",
+    [42] =  "AD_LVDSTX3_MONCLK",
+    [43] =  "AD_PLLGP_TST_CK_CKSYS",
+    [44] =  "AD_SSUSB_48M_CK_CKSYS",
+    [45] =  "AD_MONREF3_CK",
+    [46] =  "AD_MONFBK3_CK",
+    [47] =  "big_clkmon_o",
+    [48] =  "DA_ARMCPU_MON_CK",
+    [49] =  "AD_CSI0_LNRC_BYTE_CLK",
+    [50] =  "AD_CSI1_LNRC_BYTE_CLK",
+    [51] =  "AD_CSI0_LNRC_4X_CLK",
+    [52] =  "AD_CSI1_LNRC_4X_CLK",
+    [53] =  "AD_CSI0_CAL_CLK",
+    [54] =  "AD_CSI1_CAL_CLK",
+    [55] =  "AD_UNIVPL_1248M_CK",
+    [56] =  "AD_MAINPLL_1092_CORE_CK",
+    [57] =  "AD_ARMCA15PLL_2002M_CORE_CK",
+    [58] =  "mcusys_arm_clk_out_all",
+    [59] =  "AD_ARMCA7PLL_1508M_CORE_CK",
+    //[60] =    "abist_clk26(0)",
+    [61] =  "AD_UNIVPLL_USB20_48M_CK",
+    [62] =  "AD_UNIVPLL_USB20_48M_CK1",
+    [63] =  "AD_UNIVPLL_USB20_48M_CK2",
+    //[64] =    "RESERVE",
+    [65] =  "AD_UNIVPLL_USB20_48M_CK3",
+    //[66] =    "abist_clk31(0)",
+    //[67] =    "abist_clk32(0)",
+    //[68] =    "abist_clk33(0)",
+    //[69] =    "abist_clk34(0)",
+    //[70] =    "abist_clk35(0)",
+    //[71] =    "abist_clk36(0)",
+    //[72] =    "abist_clk37(0)",
+    //[73] =    "abist_clk38(0)",
+    //[74] =    "abist_clk39(0)",
+    //[75] =    "abist_clk40(0)",
+    //[76] =    "abist_clk41(0)",
+    [77] =  "AD_LVDSTX1_MONCLK",
+    [78] =  "AD_MONREF1_CK",
+    [79] =  "AD_MONFBK1_CK",
+    //[80] =    "abist_clk45(0)",
+    //[81] =    "abist_clk46(0)",
+    //[82] =    "abist_clk47(0)",
+    //[83] =    "abist_clk48(0)",
+    //[84] =    "abist_clk49(0)",
+    [85] =  "trng_freq_debug_out0",
+    [86] =  "trng_freq_debug_out1",
+    [87] =  "AD_DSI0_LNTC_DSICLK_CKSYS",
+    [88] =  "AD_DSI0_MPLL_TST_CK_CKSYS",
+    [89] =  "AD_DSI1_LNTC_DSICLK_CKSYS",
+    [90] =  "AD_DSI1_MPLL_TST_CK_CKSYS",
+    [91] =  "ddr_clk_freq_meter[0]",
+    [92] =  "ddr_clk_freq_meter[1]",
+    [93] =  "ddr_clk_freq_meter[2]",
+    [94] =  "ddr_clk_freq_meter[3]",
+    [95] =  "ddr_clk_freq_meter[4]",
+    [96] =  "ddr_clk_freq_meter[5]",
+    [97] =  "ddr_clk_freq_meter[6]",
+    [98] =  "ddr_clk_freq_meter[7]",
+    [99] =  "ddr_clk_freq_meter[8]",
+    [100] = "ddr_clk_freq_meter[9]",
+    [101] = "ddr_clk_freq_meter[10]",
+    [102] = "ddr_clk_freq_meter[11]",
+    [103] = "ddr_clk_freq_meter[12]",
+    [104] = "ddr_clk_freq_meter[13]",
+    [105] = "ddr_clk_freq_meter[14]",
+    [106] = "ddr_clk_freq_meter[15]",
+    [107] = "ddr_clk_freq_meter[16]",
+    [108] = "ddr_clk_freq_meter[17]",
+    [109] = "ddr_clk_freq_meter[18]",
+    [110] = "ddr_clk_freq_meter[19]",
+    [111] = "ddr_clk_freq_meter[20]",
+    [112] = "ddr_clk_freq_meter[21]",
+    [113] = "ddr_clk_freq_meter[22]",
+    [114] = "ddr_clk_freq_meter[23]",
+    [115] = "ddr_clk_freq_meter[24]",
+    [116] = "ddr_clk_freq_meter[25]",
+    [117] = "ddr_clk_freq_meter[26]",
+    [118] = "ddr_clk_freq_meter[27]",
+    [119] = "ddr_clk_freq_meter[28]",
+    [120] = "ddr_clk_freq_meter[29]",
+    [121] = "ddr_clk_freq_meter[30]",
+    [122] = "ddr_clk_freq_meter[31]",
+    [123] = "ddr_clk_freq_meter[32]",
+    [124] = "ddr_clk_freq_meter[33]",
+    [125] = "ddr_clk_freq_meter[34]",
+    //[126] = "abist_clk91(0)",
+    //[127] = "abist_clk92(0)",
+};
+
+static const char *ckgen_clk[] = {
+    [1] =  "hf_faxi_ck",
+    [2] =  "hd_faxi_ck",
+    [3] =  "hf_fscam_ck",
+    [4] =  "hf_fddrphycfg_ck",
+    [5] =  "hf_fmm_ck",
+    [6] =  "f_fpwm_ck",
+    [7] =  "hf_fvdec_ck",
+    [8] =  "hf_fvenc_ck",
+    [9] =  "hf_fmfg_ck",
+    [10] = "hf_fcamtg_ck",
+    [11] = "f_fuart_ck",
+    [12] = "hf_fspi_ck",
+    [13] = "f_fusb20_ck",
+    [14] = "f_fusb30_ck",
+    [15] = "hf_fmsdc50_0_hclk_ck",
+    [16] = "hf_fmsdc50_0_ck",
+    [17] = "hf_fmsdc30_1_ck",
+    [18] = "hf_fmsdc30_2_ck",
+    [19] = "hf_fmsdc30_3_ck",
+    [20] = "hf_faudio_ck",
+    [21] = "hf_faud_intbus_ck",
+    [22] = "hf_fpmicspi_ck",
+    [23] = "hf_fdpilvds1_ck",
+    [24] = "hf_fatb_ck",
+    [25] = "hf_fnr_ck",
+    [26] = "hf_firda_ck",
+    [27] = "hf_fcci400_ck",
+    [28] = "hf_faud_1_ck",
+    [29] = "hf_faud_2_ck",
+    [30] = "hf_fmem_mfg_in_as_ck",
+    [31] = "hf_faxi_mfg_in_as_ck",
+    [32] = "f_frtc_ck",
+    [33] = "f_f26m_ck",
+    [34] = "f_f32k_md1_ck",
+    [35] = "f_frtc_conn_ck",
+    [36] = "hg_fmipicfg_ck",
+    [37] = "hd_haxi_nli_ck",
+    [38] = "hd_qaxidcm_ck",
+    [39] = "f_ffpc_ck",
+    [40] = "f_fckbus_ck_scan",
+    [41] = "f_fckrtc_ck_scan",
+    [42] = "hf_flvds_pxl_ck",
+    [43] = "hf_flvds_cts_ck",
+    [44] = "hf_fdpilvds_ck",
+    [45] = "hf_flvds1_pxl_ck",
+    [46] = "hf_flvds1_cts_ck",
+    [47] = "hf_fhdcp_ck",
+    [48] = "hf_fmsdc50_3_hclk_ck",
+    [49] = "hf_fhdcp_24m_ck",
+    [50] = "hf_fmsdc0p_aes_ck",
+    [51] = "hf_fgcpu_ck",
+    [52] = "hf_fmem_ck",
+    [53] = "hf_fi2so1_mck",
+    [54] = "hf_fcam2tg_ck",
+    [55] = "hf_fether_125m_ck",
+    [56] = "hf_fapll2_ck",
+    [57] = "hf_fa2sys_hp_ck",
+    [58] = "hf_fasm_l_ck",
+    [59] = "hf_fspislv_ck",
+    [60] = "hf_ftdmo1_mck",
+    [61] = "hf_fasm_h_ck",
+    [62] = "hf_ftdmo0_mck",
+    [63] = "hf_fa1sys_hp_ck",
+    //[64] = "Reserved",
+    [65] = "hf_fasm_m_ck",
+    [66] = "hf_fapll_ck",
+    [67] = "hf_fspinor_ck",
+    [68] = "hf_fpe2_mac_p0_ck",
+    [69] = "hf_fjpgdec_ck",
+    [70] = "hf_fpwm_infra_ck",
+    [71] = "hf_fnfiecc_ck",
+    [72] = "hf_fether_50m_rmii_ck",
+    [73] = "hf_fi2c_ck",
+    [74] = "hf_fcmsys_ck",
+    [75] = "hf_fpe2_mac_p1_ck",
+    [76] = "hf_fdi_ck",
+    [77] = "hf_fi2si3_mck",
+    [78] = "hf_fether_50m_ck",
+    [79] = "hf_fi2si2_mck",
+    [80] = "hf_fi2so3_mck",
+    [81] = "hf_ftvd_ck",
+    [82] = "hf_fnfi2x_ck",
+    [83] = "hf_fi2si1_mck",
+    [84] = "hf_fi2so2_mck",
+};
+#endif /* FMETER_EN */
+
+/* after pmic_init */
+void mt_pll_post_init(void)
+{
+#if FMETER_EN
+    unsigned int temp;
+#endif
+
+    setbits32(INFRA_APB_ASYNC_STA, (0x1 << 19));
+
+    /* MTCMOS */
+    spm_mtcmos_poweron_config_enable();
+#if ALL_CLK_ON
+    spm_mtcmos_display_power_on();
+    spm_mtcmos_mfg_power_on();
+    spm_mtcmos_protect_mfg_bus();
+    spm_mtcmos_mfg_sc1_power_on();
+    spm_mtcmos_mfg_sc2_power_on();
+    spm_mtcmos_mfg_sc3_power_on();
+    spm_mtcmos_isp_power_on();
+    spm_mtcmos_vdec_power_on();
+    spm_mtcmos_usb2_power_on();
+    spm_mtcmos_venc_power_on();
+    spm_mtcmos_audio_power_on();
+    spm_mtcmos_usb_power_on();
+#endif /* ALL_CLK_ON */
+
+    /* SUBSYS_CG */
+#if ALL_CLK_ON
+    clrbits32(CLK_AUDDIV_0, 0x000000ff);
+
+    setbits32(CLK_CG_EN_CFG, 0x00000007);
+
+    writel_r(INFRA_PDN_CLR, 0x07050141);
+
+    writel_r(PERI_GLOBALCON_PDN0_CLR, 0x7ff7e7ff);
+
+    writel_r(PERI_GLOBALCON_PDN1_CLR, 0x0001dffa);
+
+    setbits32(PERI_MSDC_CLK_EN, 0x000000ff);
+
+    writel_r(MFG_CG_CLR, 0x00000001);
+
+    writel_r(MMSYS_CG0_CLR, 0xffffffff);
+
+    writel_r(MMSYS_CG1_CLR, 0xffe70fff);
+
+    writel_r(MMSYS_CG2_CLR, 0x0000007f);
+
+    clrbits32(IMG_CG, 0x00000f6f);
+
+    clrbits32(BDP_DISPSYS_CG_CON0, 0x7ffffe3f);
+
+    writel_r(VDEC_CKEN_SET, 0x00000001);
+
+    writel_r(VDEC_LARB1_CKEN_SET, 0x00000003);
+
+    writel_r(VENC_CG_SET, 0x00001011);
+
+    writel_r(JPGDEC_CG_SET, 0x00000011);
+#endif /* ALL_CLK_ON */
+
+    /* only trun off those clocks not modeled in CCF */
+    writel_r(INFRA_PDN_SET, 0x00040000);
+    writel_r(DEVAPC_PDN_SET, 0x00000001);
+    writel_r(TRNG_PDN_SET, 0x00000001);
+    writel_r(PERI_GLOBALCON_PDN0_SET, 0x00040000);
+    writel_r(PERI_GLOBALCON_PDN1_SET, 0x00000490);
+    writel_r(MMSYS_CG0_SET, 0x60000000);
+    setbits32(IMG_CG, 0x00000066);
+
+    setbits32(CLK_SCP_CFG_0, 0x205);    /* enable scpsys clock off control */
+    setbits32(CLK_SCP_CFG_1, 0x15);     /* enable scpsys clock dcm, clock sel control */
+
+    clrbits32(AP_PLL_CON3, 0x77773);    /* set Univ, Main, ARMCA35 PLL PLLs HW control */
+
+    clrbits32(AP_PLL_CON4, 0x7);    /* set Univ, Main, ARMCA35 PLL PLLs HW control */
+
+#if FMETER_EN
+    dprintf(CRITICAL, "mt_get_cpu_freq(): %d KHz\n", mt_get_cpu_freq());
+    dprintf(CRITICAL, "mt_get_bus_freq(): %d KHz\n", mt_get_bus_freq());
+
+    dprintf(CRITICAL, "abist:\n");
+
+    for (temp = 0; temp < ARRAY_SIZE(abist_clk); temp++) {
+        if (!abist_clk[temp])
+            continue;
+
+        dprintf(CRITICAL, "%2u: %s: %d KHz\n", temp, abist_clk[temp],
+                mt_fmeter_abist(temp, 0, 0));
+    }
+
+    dprintf(CRITICAL, "ckegen:\n");
+
+    for (temp = 0; temp < ARRAY_SIZE(ckgen_clk); temp++) {
+        if (!ckgen_clk[temp])
+            continue;
+
+        dprintf(CRITICAL, "%2u: %s: %d KHz\n", temp, ckgen_clk[temp],
+                mt_fmeter_ckgen(temp, 0));
+    }
+#endif /* FMETER_EN */
+}
+
+void mt_pll_init(void)
+{
+    int ca35_freq, ca72_freq;
+
+    /* AP_PLL_CON0, CLKSQ_STB_CON0, PLL_ISO_CON0 use default */
+
+    writel_r(AP_PLL_CON6, 0x00000000);   /* ISO, PWR delay mode */
+
+    /* xPLL PWR ON */
+
+    setbits32(ARMCA35PLL_PWR_CON0, 0x1);
+
+    setbits32(ARMCA72PLL_PWR_CON0, 0x1);
+
+    setbits32(MAINPLL_PWR_CON0, 0x1);
+
+    setbits32(UNIVPLL_PWR_CON0, 0x1);
+
+    setbits32(MMPLL_PWR_CON0, 0x1);
+
+    setbits32(MSDCPLL_PWR_CON0, 0x1);
+
+    setbits32(VENCPLL_PWR_CON0, 0x1);
+
+    setbits32(TVDPLL_PWR_CON0, 0x1);
+
+    setbits32(ETHERPLL_PWR_CON0, 0x1);
+
+    setbits32(VCODECPLL_PWR_CON0, 0x1);
+
+    setbits32(APLL1_PWR_CON0, 0x1);
+
+    setbits32(APLL2_PWR_CON0, 0x1);
+
+    setbits32(LVDSPLL_PWR_CON0, 0x1);
+
+    setbits32(LVDSPLL2_PWR_CON0, 0x1);
+
+    setbits32(MSDCPLL2_PWR_CON0, 0x1);
+
+    udelay(5);  /* wait for xPLL_PWR_ON ready (min delay is 0.1us) */
+
+    /* xPLL ISO Disable */
+
+    clrbits32(ARMCA35PLL_PWR_CON0, 0x2);
+
+    clrbits32(ARMCA72PLL_PWR_CON0, 0x2);
+
+    clrbits32(MAINPLL_PWR_CON0, 0x2);
+
+    clrbits32(UNIVPLL_PWR_CON0, 0x2);
+
+    clrbits32(MMPLL_PWR_CON0, 0x2);
+
+    clrbits32(MSDCPLL_PWR_CON0, 0x2);
+
+    clrbits32(VENCPLL_PWR_CON0, 0x2);
+
+    clrbits32(TVDPLL_PWR_CON0, 0x2);
+
+    clrbits32(ETHERPLL_PWR_CON0, 0x2);
+
+    clrbits32(VCODECPLL_PWR_CON0, 0x2);
+
+    clrbits32(APLL1_PWR_CON0, 0x2);
+
+    clrbits32(APLL2_PWR_CON0, 0x2);
+
+    clrbits32(LVDSPLL_PWR_CON0, 0x2);
+
+    clrbits32(LVDSPLL2_PWR_CON0, 0x2);
+
+    clrbits32(MSDCPLL2_PWR_CON0, 0x2);
+
+    /* xPLL Frequency Set */
+    ca35_freq = det_ca35_freq();
+    switch (ca35_freq) {
+        case CA35_SPD_806MHZ:
+            writel_r(ARMCA35PLL_CON1, 0xBE000000);   /* 806MHz */
+            break;
+        case CA35_SPD_1001MHZ:
+            writel_r(ARMCA35PLL_CON1, 0xCD000000);   /* 1001Mhz */
+            break;
+        case CA35_SPD_1196MHZ:
+            writel_r(ARMCA35PLL_CON1, 0xDC000000);   /* 1196MHz */
+            break;
+        default:
+            assert(false);
+    }
+
+    ca72_freq = det_ca72_freq();
+    switch (ca72_freq) {
+        case CA72_SPD_1001MHZ:
+            writel_r(ARMCA72PLL_CON1, 0xA6800000);   /* 1001MHz */
+            break;
+        case CA72_SPD_1105MHZ:
+            writel_r(ARMCA72PLL_CON1, 0xAA800000);   /* 1105MHz */
+            break;
+        case CA72_SPD_1196MHZ:
+            writel_r(ARMCA72PLL_CON1, 0xAE000000);   /* 1196MHz */
+            break;
+        case CA72_SPD_1391MHZ:
+            writel_r(ARMCA72PLL_CON1, 0xB5800000);   /* 1391MHz */
+            break;
+        case CA72_SPD_1495MHZ:
+            writel_r(ARMCA72PLL_CON1, 0xB9800000);   /* 1495MHz */
+            break;
+        case CA72_SPD_1599MHZ:
+            writel_r(ARMCA72PLL_CON1, 0xBD800000);   /* 1599MHz */
+            break;
+        default:
+            assert(false);
+    }
+
+    writel_r(MAINPLL_CON1, 0xD4000000);  /* 1092MHz */
+
+    writel_r(UNIVPLL_CON1, 0xE0000000);  /* 1248MHz */
+
+    writel_r(MMPLL_CON1, 0xCD000000);    /* 500.5MHz */
+
+    writel_r(MSDCPLL_CON1, 0xBD89D89D);  /* 400MHz */
+
+    writel_r(VENCPLL_CON1, 0xBA762762);  /* 380 MHz */
+
+    writel_r(TVDPLL_CON1, 0xC2000000);   /* 429MHz */
+
+    writel_r(ETHERPLL_CON1, 0xCCEC4EC4); /* 50MHz */
+
+    writel_r(VCODECPLL_CON1, 0xC4000000);    /* 442 MHz */
+
+    writel_r(APLL1_CON1, 0xBC7EA932);    /* 196.608MHz */
+
+    writel_r(APLL2_CON1, 0xB7945EA6);    /* 180.6336MHz */
+
+    writel_r(LVDSPLL_CON1, 0xAE276276);  /* 150MHz */
+
+    writel_r(LVDSPLL2_CON1, 0xAE276276); /* 150MHz */
+
+    writel_r(MSDCPLL2_CON1, 0xBD89D89D); /* 400MHz */
+
+    /* xPLL Frequency Enable */
+
+    setbits32(ARMCA35PLL_CON0, 0x1);
+
+    setbits32(ARMCA72PLL_CON0, 0x1);
+
+    setbits32(MAINPLL_CON0, 0x1);
+
+    clrsetbits32(UNIVPLL_CON0, 0x100, 0x1); /* Integer Mode */
+
+    setbits32(MMPLL_CON0, 0x1);
+
+    setbits32(MSDCPLL_CON0, 0x1);
+
+    setbits32(VENCPLL_CON0, 0x1);
+
+    setbits32(TVDPLL_CON0, 0x1);
+
+    setbits32(ETHERPLL_CON0, 0x1);
+
+    setbits32(VCODECPLL_CON0, 0x1);
+
+    setbits32(APLL1_CON0, 0x1);
+
+    setbits32(APLL2_CON0, 0x1);
+
+    setbits32(LVDSPLL_CON0, 0x1);
+
+    setbits32(LVDSPLL2_CON0, 0x1);
+
+    setbits32(MSDCPLL2_CON0, 0x1);
+
+    udelay(40); /* wait for PLL stable (min delay is 20us) */
+
+    /* xPLL DIV RSTB */
+
+    setbits32(ARMCA35PLL_CON0, 0x01000000);
+
+    setbits32(MAINPLL_CON0, 0x01000000);
+
+    setbits32(UNIVPLL_CON0, 0x01000000);
+
+    /* MCUCFG CLKMUX */
+
+    clrsetbits32(mp0_pll_divider_cfg, 0x00000600, 0x00000200);
+
+    clrsetbits32(mp2_pll_divider_cfg, 0x00000600, 0x00000200);
+
+    clrsetbits32(bus_pll_divider_cfg, 0x00000600, 0x00000200);
+
+    setbits32(TOP_DCMCTL, 0x1); /* enable infrasys clock div */
+
+    setbits32(DCM_CFG, 0x80);   /* enable AXI bus clock DCM */
+
+    writel_r(MBIST_CFG_2, 0x67D2A357); /* enable reset deglitch */
+
+    setbits32(CLK_MODE, 0x1);
+    clrbits32(CLK_MODE, 0x1);   /* enable TOPCKGEN */
+
+    /* TOP CLKMUX */
+
+    clrsetbits32(CLK_CFG_0, 0x07000107, 0x01000101);
+
+    clrsetbits32(CLK_CFG_1, 0x0f0f0f03, 0x01010101);
+
+    clrsetbits32(CLK_CFG_2, 0x0307010f, 0x0101000a);
+
+    clrsetbits32(CLK_CFG_3, 0x070f0703, 0x02010101);
+
+    clrsetbits32(CLK_CFG_4, 0x07030f07, 0x01000202);
+
+    clrsetbits32(CLK_CFG_5, 0x07030707, 0x01010101);
+
+    clrsetbits32(CLK_CFG_6, 0x0307030f, 0x01040107);
+
+    clrsetbits32(CLK_CFG_7, 0x03030303, 0x01010101);
+
+    clrsetbits32(CLK_CFG_8, 0x07070707, 0x01050506);
+
+#if MTK_CLK32K_EXT_REMOVAL_SUPPORT
+    clrsetbits32(CLK_CFG_9, 0x03030307, 0x00030302);
+#else
+    clrsetbits32(CLK_CFG_9, 0x03030307, 0x01030302);
+#endif
+
+    clrsetbits32(CLK_CFG_10, 0x070f0f0f, 0x02060103);
+
+    clrsetbits32(CLK_CFG_11, 0x03030307, 0x03030302);
+
+    clrsetbits32(CLK_CFG_12, 0x03030303, 0x02020202);
+
+    clrsetbits32(CLK_CFG_13, 0x03030303, 0x02020202);
+
+    clrsetbits32(CLK_CFG_14, 0x070f0303, 0x06030101);
+
+    clrsetbits32(CLK_CFG_15, 0x03070f03, 0x01010a01);
+
+    clrsetbits32(CLK_CFG_16, 0x07030307, 0x01010104);
+
+    clrsetbits32(CLK_CFG_17, 0x00000007, 0x00000001);
+}
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/pmic/pmic.c b/src/bsp/lk/platform/mediatek/mt2712/drivers/pmic/pmic.c
new file mode 100644
index 0000000..98375e7
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/pmic/pmic.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020 MediaTek Inc.
+ *
+ * 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 "mtk_i2c.h"
+
+#define CORE_BUS_NUM 0
+#define CPU_BUS_NUM 1
+#define DEV_ADDR 0x18
+#define SPEED 1000
+
+int rtq2134_set_bcpu_voltage(int target_vol)
+{
+    int read_vol;
+    uint8_t rw_buffer[2];
+
+    rw_buffer[0] = 0x48;
+    rw_buffer[1] = (target_vol - 300) / 5;
+    dprintf(CRITICAL, "[%s]Target Vol: %d(0x%x)\n", __func__, target_vol, rw_buffer[1]);
+    i2c_cust_write(CPU_BUS_NUM, DEV_ADDR, SPEED, rw_buffer, 2);
+    i2c_cust_write(CPU_BUS_NUM, DEV_ADDR, SPEED, rw_buffer, 1);
+    i2c_cust_read(CPU_BUS_NUM, DEV_ADDR, SPEED, &rw_buffer[1], 1);
+    read_vol =  rw_buffer[1] * 5 + 300;
+    dprintf(CRITICAL, "[%s]Current Vol: %d(0x%x)\n", __func__, read_vol, rw_buffer[1]);
+
+    return read_vol;
+}
+
+int rtq2134_set_lcpu_voltage(int target_vol)
+{
+    int read_vol;
+    uint8_t rw_buffer[2];
+
+    rw_buffer[0] = 0x62;
+    rw_buffer[1] = (target_vol - 300) / 5;
+    dprintf(CRITICAL, "[%s]Target Vol: %d(0x%x)\n", __func__, target_vol, rw_buffer[1]);
+    i2c_cust_write(CPU_BUS_NUM, DEV_ADDR, SPEED, rw_buffer, 2);
+    i2c_cust_write(CPU_BUS_NUM, DEV_ADDR, SPEED, rw_buffer, 1);
+    i2c_cust_read(CPU_BUS_NUM, DEV_ADDR, SPEED, &rw_buffer[1], 1);
+    read_vol =  rw_buffer[1] * 5 + 300;
+    dprintf(CRITICAL, "[%s]Current Vol: %d(0x%x)\n", __func__, read_vol, rw_buffer[1]);
+
+    return read_vol;
+}
+
+int rtq2134_set_core_voltage(int target_vol)
+{
+    int read_vol;
+    uint8_t rw_buffer[2];
+
+    rw_buffer[0] = 0x48;
+    rw_buffer[1] = (target_vol - 300) / 5;
+    dprintf(CRITICAL, "[%s]Target Vol: %d(0x%x)\n", __func__, target_vol, rw_buffer[1]);
+    i2c_cust_write(CORE_BUS_NUM, DEV_ADDR, SPEED, rw_buffer, 2);
+    i2c_cust_write(CORE_BUS_NUM, DEV_ADDR, SPEED, rw_buffer, 1);
+    i2c_cust_read(CORE_BUS_NUM, DEV_ADDR, SPEED, &rw_buffer[1], 1);
+    read_vol =  rw_buffer[1] * 5 + 300;
+    dprintf(CRITICAL, "[%s]Current Vol: %d(0x%x)\n", __func__, read_vol, rw_buffer[1]);
+
+    return read_vol;
+}
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/rules.mk b/src/bsp/lk/platform/mediatek/mt2712/drivers/rules.mk
new file mode 100644
index 0000000..2c3c64b
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/rules.mk
@@ -0,0 +1,48 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS += \
+    $(COMMON_PLAT)/drivers/uart/uart.c \
+    $(COMMON_PLAT)/drivers/usb/mtu3.c \
+    $(COMMON_PLAT)/drivers/usb/mtu3_qmu.c \
+    $(COMMON_PLAT)/drivers/usb/usbphy.c \
+    $(LOCAL_DIR)/dcm/dcm.c \
+    $(LOCAL_DIR)/efuse/mtk_devinfo.c \
+    $(LOCAL_DIR)/gpio/mt_gpio.c \
+    $(LOCAL_DIR)/key/mtk_key.c \
+    $(LOCAL_DIR)/nor/mtk_nor.c \
+    $(LOCAL_DIR)/pll/pll.c \
+    $(LOCAL_DIR)/spm/spm_mtcmos.c \
+    $(LOCAL_DIR)/trng/mtk_trng.c \
+    $(LOCAL_DIR)/wdt/mtk_wdt.c \
+
+MODULE_DEPS += \
+    lib/bio \
+    lib/cksum \
+    lib/fdt \
+    lib/mempool \
+    lib/partition \
+    lib/wrapper \
+
+ifeq ($(ENABLE_SCP_LOAD),1)
+MODULE_DEPS += $(LOCAL_DIR)/scp
+endif
+
+ifeq ($(LK_AS_BL33),0)
+MODULE_DEPS += \
+    $(LOCAL_DIR)/../../../../../dramk_2712/$(DRAM_TYPE)
+endif
+
+ifeq ($(LK_AS_BL33),1)
+MODULE_SRCS += \
+    $(LOCAL_DIR)/efuse/mtk_efuse.c \
+    $(LOCAL_DIR)/pmic/pmic.c \
+
+MODULE_EXTRA_OBJS += $(LOCAL_DIR)/../../../../../lk_ext_mod/platform/mt2712/drivers/efuse/$(ARCH)/VM/libefuse.o
+endif
+
+EXTRA_OBJS += $(LOCAL_DIR)/security/libsec.a
+MODULE_EXTRA_OBJS += $(LOCAL_DIR)/../../../../../lk_ext_mod/platform/mt2712/drivers/devinfo/libdevinfo.o
+
+include make/module.mk
+include $(LOCAL_DIR)/audio/rules.mk
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/scp/mt_scp.c b/src/bsp/lk/platform/mediatek/mt2712/drivers/scp/mt_scp.c
new file mode 100644
index 0000000..40bde43
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/scp/mt_scp.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <arch/arm64.h>
+#include <platform/mt2712.h>
+#include <platform/mt_reg_base.h>
+#include <platform/mt_scp.h>
+#include <reg.h>
+#include <sys/types.h>
+
+#define BIT(bit) (1UL << (bit))
+
+/* scp control */
+#define CMSYS_RESET_CTL     0x0
+#define CPU_RST_SW          BIT(0)
+#define BUS_RST_SW	    BIT(1)
+#define HBUS_RST_SW	    BIT(2)
+
+#define CMSYS_CLKGAT_CTL    0x8
+#define CPUCK_EN            0x1
+
+void start_scpsys(void)
+{
+    u32 reg;
+
+    /*
+     * These buses should be reset before the CM4 start running,
+     * in case some bus issues will be happened when system reboot
+     */
+    reg = readl(SCP_BASE_CFG + CMSYS_RESET_CTL);
+    reg &= ~(BUS_RST_SW | HBUS_RST_SW);
+    writel(reg, SCP_BASE_CFG + CMSYS_RESET_CTL);
+    DSB;
+
+    reg = readl(SCP_BASE_CFG + CMSYS_RESET_CTL);
+    reg |= BUS_RST_SW | HBUS_RST_SW;
+    writel(reg, SCP_BASE_CFG + CMSYS_RESET_CTL);
+
+    reg = readl(SCP_BASE_CFG + CMSYS_CLKGAT_CTL);
+    reg |= CPUCK_EN;
+    writel(reg, SCP_BASE_CFG + CMSYS_CLKGAT_CTL);
+
+    reg = readl(SCP_BASE_CFG + CMSYS_RESET_CTL);
+    reg |= CPU_RST_SW;
+    writel(reg, SCP_BASE_CFG + CMSYS_RESET_CTL);
+    DSB;
+}
+
+void stop_scpsys(void)
+{
+    u32 reg;
+
+    reg = readl(SCP_BASE_CFG + CMSYS_RESET_CTL);
+    reg &= ~CPU_RST_SW;
+    writel(reg, SCP_BASE_CFG + CMSYS_RESET_CTL);
+
+    reg = readl(SCP_BASE_CFG + CMSYS_CLKGAT_CTL);
+    reg &= ~CPUCK_EN;
+    writel(reg, SCP_BASE_CFG + CMSYS_CLKGAT_CTL);
+    DSB;
+}
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/scp/rules.mk b/src/bsp/lk/platform/mediatek/mt2712/drivers/scp/rules.mk
new file mode 100644
index 0000000..97bb27d
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/scp/rules.mk
@@ -0,0 +1,7 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS += \
+    $(LOCAL_DIR)/mt_scp.c
+
+include make/module.mk
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/security/libsec.a b/src/bsp/lk/platform/mediatek/mt2712/drivers/security/libsec.a
new file mode 100644
index 0000000..41b5167
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/security/libsec.a
Binary files differ
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/spm/spm_mtcmos.c b/src/bsp/lk/platform/mediatek/mt2712/drivers/spm/spm_mtcmos.c
new file mode 100644
index 0000000..665835b
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/spm/spm_mtcmos.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <platform/mt_infracfg.h>
+#include <platform/reg_utils.h>
+
+#include "spm_mtcmos_internal.h"
+
+struct power_domain_data {
+    void *pwr_con;
+    unsigned int pwr_sta_mask;
+    unsigned int sram_pdn_mask;
+    unsigned int sram_ack_mask;
+};
+
+static void mtcmos_power_on(const struct power_domain_data *pd)
+{
+    setbits32(pd->pwr_con, PWR_ON);
+    setbits32(pd->pwr_con, PWR_ON_2ND);
+
+    while (!(readl(&mtk_spm->pwr_status) & pd->pwr_sta_mask) ||
+           !(readl(&mtk_spm->pwr_status_2nd) & pd->pwr_sta_mask))
+        continue;
+
+    clrbits32(pd->pwr_con, PWR_CLK_DIS);
+    clrbits32(pd->pwr_con, PWR_ISO);
+    setbits32(pd->pwr_con, PWR_RST_B);
+    clrbits32(pd->pwr_con, pd->sram_pdn_mask);
+
+    while (readl(pd->pwr_con) & pd->sram_ack_mask)
+        continue;
+}
+
+void spm_mtcmos_poweron_config_enable(void)
+{
+    writel((SPM_REGWR_CFG_KEY | SPM_REGWR_EN), &mtk_spm->poweron_config_set);
+}
+
+void spm_mtcmos_protect_mfg_bus(void)
+{
+    writel(MFG_PROT_MASK, TOPAXI_PROT_EN_CLR);
+    while (readl(TOPAXI_PROT_STA1) & MFG_PROT_MASK)
+        continue;
+}
+
+void spm_mtcmos_display_power_on(void)
+{
+    static const struct power_domain_data disp = {
+        .pwr_con = &mtk_spm->dis_pwr_con,
+        .pwr_sta_mask = DIS_PWR_STA_MASK,
+        .sram_pdn_mask = DIS_SRAM_PDN,
+        .sram_ack_mask = DIS_SRAM_ACK,
+    };
+
+    mtcmos_power_on(&disp);
+}
+
+void spm_mtcmos_audio_power_on(void)
+{
+    static const struct power_domain_data audio = {
+        .pwr_con = &mtk_spm->audio_pwr_con,
+        .pwr_sta_mask = AUD_PWR_STA_MASK,
+        .sram_pdn_mask = AUD_SRAM_PDN,
+        .sram_ack_mask = AUD_SRAM_ACK,
+    };
+
+    mtcmos_power_on(&audio);
+}
+
+void spm_mtcmos_mfg_power_on(void)
+{
+    static const struct power_domain_data mfg = {
+        .pwr_con = &mtk_spm->mfg_pwr_con,
+        .pwr_sta_mask = MFG_PWR_STA_MASK,
+        .sram_pdn_mask = MFG_SRAM_PDN,
+        .sram_ack_mask = MFG_SRAM_ACK,
+    };
+
+    mtcmos_power_on(&mfg);
+}
+
+void spm_mtcmos_mfg_sc1_power_on(void)
+{
+    static const struct power_domain_data mfg_sc1 = {
+        .pwr_con = &mtk_spm->mfg_sc1_pwr_con,
+        .pwr_sta_mask = MFG_SC1_PWR_STA_MASK,
+        .sram_pdn_mask = MFG_SRAM_PDN,
+        .sram_ack_mask = MFG_SRAM_ACK,
+    };
+
+    mtcmos_power_on(&mfg_sc1);
+}
+
+void spm_mtcmos_mfg_sc2_power_on(void)
+{
+    static const struct power_domain_data mfg_sc2 = {
+        .pwr_con = &mtk_spm->mfg_sc2_pwr_con,
+        .pwr_sta_mask = MFG_SC2_PWR_STA_MASK,
+        .sram_pdn_mask = MFG_SRAM_PDN,
+        .sram_ack_mask = MFG_SRAM_ACK,
+    };
+
+    mtcmos_power_on(&mfg_sc2);
+}
+
+void spm_mtcmos_mfg_sc3_power_on(void)
+{
+    static const struct power_domain_data mfg_sc3 = {
+        .pwr_con = &mtk_spm->mfg_sc3_pwr_con,
+        .pwr_sta_mask = MFG_SC3_PWR_STA_MASK,
+        .sram_pdn_mask = MFG_SRAM_PDN,
+        .sram_ack_mask = MFG_SRAM_ACK,
+    };
+
+    mtcmos_power_on(&mfg_sc3);
+}
+
+void spm_mtcmos_isp_power_on(void)
+{
+    static const struct power_domain_data isp = {
+        .pwr_con = &mtk_spm->isp_pwr_con,
+        .pwr_sta_mask = ISP_PWR_STA_MASK,
+        .sram_pdn_mask = ISP_SRAM_PDN,
+        .sram_ack_mask = ISP_SRAM_ACK,
+    };
+
+    mtcmos_power_on(&isp);
+}
+
+void spm_mtcmos_vdec_power_on(void)
+{
+    static const struct power_domain_data vdec = {
+        .pwr_con = &mtk_spm->vdec_pwr_con,
+        .pwr_sta_mask = VDE_PWR_STA_MASK,
+        .sram_pdn_mask = VDE_SRAM_PDN,
+        .sram_ack_mask = VDE_SRAM_ACK,
+    };
+
+    mtcmos_power_on(&vdec);
+}
+
+void spm_mtcmos_venc_power_on(void)
+{
+    static const struct power_domain_data venc = {
+        .pwr_con = &mtk_spm->ven_pwr_con,
+        .pwr_sta_mask = VEN_PWR_STA_MASK,
+        .sram_pdn_mask = VEN_SRAM_PDN,
+        .sram_ack_mask = VEN_SRAM_ACK,
+    };
+
+    mtcmos_power_on(&venc);
+}
+
+void spm_mtcmos_usb_power_on(void)
+{
+    static const struct power_domain_data usb = {
+        .pwr_con = &mtk_spm->usb_pwr_con,
+        .pwr_sta_mask = USB_PWR_STA_MASK,
+        .sram_pdn_mask = USB_SRAM_PDN,
+        .sram_ack_mask = USB_SRAM_ACK,
+    };
+
+    mtcmos_power_on(&usb);
+}
+
+void spm_mtcmos_usb2_power_on(void)
+{
+    static const struct power_domain_data usb2 = {
+        .pwr_con = &mtk_spm->usb2_pwr_con,
+        .pwr_sta_mask = USB2_PWR_STA_MASK,
+        .sram_pdn_mask = USB2_SRAM_PDN,
+        .sram_ack_mask = USB2_SRAM_ACK,
+    };
+
+    mtcmos_power_on(&usb2);
+}
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/spm/spm_mtcmos_internal.h b/src/bsp/lk/platform/mediatek/mt2712/drivers/spm/spm_mtcmos_internal.h
new file mode 100644
index 0000000..dcafd6a
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/spm/spm_mtcmos_internal.h
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once
+
+#include <platform/mt_reg_base.h>
+
+enum {
+    SPM_PROJECT_CODE    = 0xb16,
+    SPM_REGWR_CFG_KEY   = (SPM_PROJECT_CODE << 16),
+    SPM_REGWR_EN        = (1U << 0),
+};
+
+enum {
+    DIS_PWR_STA_MASK        = 0x1 << 3,
+    MFG_PWR_STA_MASK        = 0x1 << 4,
+    ISP_PWR_STA_MASK        = 0x1 << 5,
+    VDE_PWR_STA_MASK        = 0x1 << 7,
+    USB2_PWR_STA_MASK       = 0x1 << 19,
+    VEN_PWR_STA_MASK        = 0x1 << 21,
+    MFG_SC1_PWR_STA_MASK    = 0x1 << 22,
+    MFG_SC2_PWR_STA_MASK    = 0x1 << 23,
+    AUD_PWR_STA_MASK        = 0x1 << 24,
+    USB_PWR_STA_MASK        = 0x1 << 25,
+    MFG_SC3_PWR_STA_MASK    = 0x1 << 30,
+};
+
+enum {
+    DIS_SRAM_PDN    = 0x1 << 8,
+    DIS_SRAM_ACK    = 0x1 << 12,
+    MFG_SRAM_PDN    = 0x1 << 8,
+    MFG_SRAM_ACK    = 0x1 << 16,
+    ISP_SRAM_PDN    = 0xf << 8,
+    ISP_SRAM_ACK    = 0x3 << 12,
+    VDE_SRAM_PDN    = 0x1 << 8,
+    VDE_SRAM_ACK    = 0x1 << 12,
+    USB2_SRAM_PDN   = 0x7 << 8,
+    USB2_SRAM_ACK   = 0x7 << 12,
+    VEN_SRAM_PDN    = 0xf << 8,
+    VEN_SRAM_ACK    = 0xf << 12,
+    AUD_SRAM_PDN    = 0xf << 8,
+    AUD_SRAM_ACK    = 0xf << 12,
+    USB_SRAM_PDN    = 0x7 << 8,
+    USB_SRAM_ACK    = 0x7 << 12,
+};
+
+enum {
+    SRAM_ISOINT_B = 1U << 6,
+    SRAM_CKISO    = 1U << 5,
+    PWR_CLK_DIS   = 1U << 4,
+    PWR_ON_2ND    = 1U << 3,
+    PWR_ON        = 1U << 2,
+    PWR_ISO       = 1U << 1,
+    PWR_RST_B     = 1U << 0
+};
+
+struct mtk_2712_spm_regs {
+    unsigned int poweron_config_set;
+    unsigned int reserved_00[3];
+    unsigned int power_on_val0;     /* 0x10 */
+    unsigned int power_on_val1;
+    unsigned int reserved_10[58];
+    unsigned int clk_settle;        /* 0x100 */
+    unsigned int reserved_20[61];
+    unsigned int mfg_sc3_pwr_con;   /* 0x1f8 */
+    unsigned int reserved_21[1];
+    unsigned int mp0_cpu0_pwr_con;  /* 0x200 */
+    unsigned int mp0_dbg_pwr_con;
+    unsigned int mp0_cputop_pwr_con;
+    unsigned int reserved_30[1];
+    unsigned int vdec_pwr_con;      /* 0x210 */
+    unsigned int mfg_pwr_con;
+    unsigned int mp0_cpu1_pwr_con;
+    unsigned int mp0_cpu2_pwr_con;
+    unsigned int mp0_cpu3_pwr_con;
+    unsigned int reserved_40[3];
+    unsigned int ven_pwr_con;       /* 0x230 */
+    unsigned int ifr_pwr_con;
+    unsigned int isp_pwr_con;
+    unsigned int dis_pwr_con;
+    unsigned int dpy_pwr_con;
+    unsigned int mp0_cputop_l2_pdn;
+    unsigned int mp0_cputop_l2_sleep;
+    unsigned int reserved_50[4];
+    unsigned int mp0_cpu0_l1_pdn;   /* 0x25c */
+    unsigned int reserved_51;
+    unsigned int mp0_cpu1_l1_pdn;
+    unsigned int reserved_52;
+    unsigned int mp0_cpu2_l1_pdn;
+    unsigned int reserved_53;
+    unsigned int mp0_cpu3_l1_pdn;
+    unsigned int reserved_54;
+    unsigned int fhc_sram_con;
+    unsigned int conn_pwr_con;
+    unsigned int md_pwr_con;
+    unsigned int reserved_60[2];
+    unsigned int mcu_pwr_con;       /* 0x290 */
+    unsigned int ifr_sramrom_con;
+    unsigned int ven2_pwr_con;
+    unsigned int audio_pwr_con;
+    unsigned int reserved_70[8];
+    unsigned int mfg_sc1_pwr_con;   /* 0x2c0 */
+    unsigned int mfg_sc2_pwr_con;
+    unsigned int md32_sram_pwr_con;
+    unsigned int usb_pwr_con;
+    unsigned int reserved_80[1];
+    unsigned int usb2_pwr_con;
+    unsigned int reserved_81[1];
+    unsigned int cpu_ext_iso;
+    unsigned int reserved_85[5];
+    unsigned int mp_pwr_con;        /* 0x2f4 */
+    unsigned int reserved_90[6];
+    unsigned int pcm_con0;          /* 0x310 */
+    unsigned int pcm_con1;
+    unsigned int pcm_im_ptr;
+    unsigned int pcm_im_len;
+    unsigned int pcm_reg_data_ini;
+    unsigned int reserved_100[7];
+    unsigned int pcm_event_vector0; /* 0x340 */
+    unsigned int pcm_event_vector1;
+    unsigned int pcm_event_vector2;
+    unsigned int pcm_event_vector3;
+    unsigned int reserved_110[1];
+    unsigned int pcm_mas_pause_mask;
+    unsigned int pcm_pwr_io_en;
+    unsigned int pcm_timer_val;
+    unsigned int pcm_timer_out;
+    unsigned int reserved_120[7];
+    unsigned int pcm_reg0_data;     /* 0x380 */
+    unsigned int pcm_reg1_data;
+    unsigned int pcm_reg2_data;
+    unsigned int pcm_reg3_data;
+    unsigned int pcm_reg4_data;
+    unsigned int pcm_reg5_data;
+    unsigned int pcm_reg6_data;
+    unsigned int pcm_reg7_data;
+    unsigned int pcm_reg8_data;
+    unsigned int pcm_reg9_data;
+    unsigned int pcm_reg10_data;
+    unsigned int pcm_reg11_data;
+    unsigned int pcm_reg12_data;
+    unsigned int pcm_reg13_data;
+    unsigned int pcm_reg14_data;
+    unsigned int pcm_reg15_data;
+    unsigned int pcm_event_reg_sta;
+    unsigned int pcm_fsm_sta;
+    unsigned int pcm_im_host_rw_ptr;
+    unsigned int pcm_im_host_rw_dat;
+    unsigned int pcm_event_vector4;
+    unsigned int pcm_event_vector5;
+    unsigned int pcm_event_vector6;
+    unsigned int pcm_event_vector7;
+    unsigned int pcm_sw_int_set;
+    unsigned int pcm_sw_int_clear;
+    unsigned int pcm_regc_wakeup_mask;
+    unsigned int reserved_130[3];
+    unsigned int pcm_mas_pause_mask2;       /* 0x3f8 */
+    unsigned int reserved_132[1];
+    unsigned int clk_con;
+    unsigned int reserved_140[127];
+    unsigned int apmcu_pwrctl;              /* 0x600 */
+    unsigned int ap_dvfs_con_set;
+    unsigned int sleep_stnby_con;
+    unsigned int pwr_status;
+    unsigned int pwr_status_2nd;
+    unsigned int sleep_mdbsi_con;
+    unsigned int reserved_150[2];
+    unsigned int rf_clk_cfg;                /* 0x620 */
+    unsigned int rf_clk_cfg_set;
+    unsigned int rf_clk_cfg_clr;
+    unsigned int reserved_160[3];
+    unsigned int spm_ap_sema;               /* 0x638 */
+    unsigned int spm_spm_sema;
+    unsigned int reserved_170[56];
+    unsigned int sleep_timer_sta;           /* 0x720 */
+    unsigned int reserved_180[15];
+    unsigned int sleep_twam_con;            /* 0x760 */
+    unsigned int sleep_twam_last_status0;
+    unsigned int sleep_twam_last_status1;
+    unsigned int sleep_twam_last_status2;
+    unsigned int sleep_twam_last_status3;
+    unsigned int sleep_twam_curr_status0;
+    unsigned int sleep_twam_curr_status1;
+    unsigned int sleep_twam_curr_status2;
+    unsigned int sleep_twam_curr_status3;
+    unsigned int sleep_twam_timer_out;
+    unsigned int sleep_twam_window_len;
+    unsigned int reserved_190[33];
+    unsigned int sleep_wakeup_event_mask;   /* 0x810 */
+    unsigned int sleep_cpu_wakeup_event;
+    unsigned int reserved_200[3];
+    unsigned int pcm_wdt_timer_val;
+    unsigned int pcm_wdt_timer_out;
+    unsigned int reserved_210[53];
+    unsigned int sleep_isr_mask;            /* 0x900 */
+    unsigned int sleep_isr_status;
+    unsigned int reserved_220[2];
+    unsigned int sleep_isr_raw_sta;         /* 0x910 */
+    unsigned int reserved_230[1];
+    unsigned int sleep_wakeup_misc;
+    unsigned int sleep_bus_protect_rdy;
+    unsigned int sleep_subsys_idle_sta;
+    unsigned int reserved_240[119];
+    unsigned int pcm_reserve;               /* 0xb00 */
+    unsigned int pcm_reserve2;
+    unsigned int pcm_flags;
+    unsigned int pcm_src_req;
+    unsigned int reserved_250;
+    unsigned int pcm_reserve3;
+    unsigned int pcm_reserve4;
+    unsigned int pcm_mmddr_mask;
+    unsigned int pcm_debug_con;
+    unsigned int pcm_wdt_latch;
+    unsigned int reserved_260[2];
+    unsigned int mp0_cpu0_irq_mask;         /* 0xb30 */
+    unsigned int mp0_cpu1_irq_mask;
+    unsigned int mp0_cpu2_irq_mask;
+    unsigned int mp0_cpu3_irq_mask;
+    unsigned int reserved_270[8];
+    unsigned int pcm_pasr_dpd_0;            /* 0xb60 */
+    unsigned int pcm_pasr_dpd_1;
+    unsigned int pcm_pasr_dpd_2;
+    unsigned int pcm_pasr_dpd_3;
+    unsigned int reserved_280[36];
+    unsigned int pcm_event_vector_en;       /* 0xc00 */
+    unsigned int pcm_event_vector8;
+    unsigned int pcm_event_vector9;
+    unsigned int pcm_event_vectora;
+    unsigned int pcm_event_vectorb;
+    unsigned int pcm_event_vectorc;
+    unsigned int pcm_event_vectord;
+    unsigned int pcm_event_vectore;
+    unsigned int pcm_event_vectorf;
+    unsigned int pcm_reserve5;
+    unsigned int pcm_reserve6;
+    unsigned int pcm_reserve7;
+    unsigned int pcm_reserve8;
+    unsigned int reserved_290[3];
+    unsigned int spmc_mp0_cpu0_con;         /* 0xc40 */
+    unsigned int spmc_mp0_cpu1_con;
+    unsigned int spmc_mp0_cpu2_con;
+    unsigned int spmc_mp0_cpu3_con;
+    unsigned int spmc_mp0_con;
+    unsigned int spmc_mp0_srm_slp;
+    unsigned int spmc_mp0_clk_dis;
+    unsigned int reserved_300[1];
+    unsigned int spmc_mp1_cpu0_con;
+    unsigned int spmc_mp1_cpu1_con;
+    unsigned int reserved_310[2];
+    unsigned int spmc_mp1_con;              /* 0xc70 */
+    unsigned int spmc_mp1_srm_slp;
+    unsigned int spmc_mp1_clk_dis;
+    unsigned int spmc_mp1_l2_flush;
+    unsigned int reserved_320[160];
+    unsigned int sleep_mp0_wfi0_en;         /* 0xf00 */
+    unsigned int sleep_mp0_wfi1_en;
+    unsigned int sleep_mp0_wfi2_en;
+    unsigned int sleep_mp0_wfi3_en;
+    unsigned int sleep_mp1_wfi0_en;
+    unsigned int sleep_mp1_wfi1_en;         /* 0xf14 */
+};
+
+_Static_assert(offsetof(struct mtk_2712_spm_regs, sleep_mp1_wfi1_en) == 0xf14,
+        "sleep_mp1_wfi1_en offset shift");
+
+static struct mtk_2712_spm_regs *const mtk_spm = (void *)SPM_BASE;
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/trng/mtk_trng.c b/src/bsp/lk/platform/mediatek/mt2712/drivers/trng/mtk_trng.c
new file mode 100644
index 0000000..f5ca1f4
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/trng/mtk_trng.c
@@ -0,0 +1,61 @@
+#include <debug.h>
+#include <reg.h>
+#include <platform/mt_reg_base.h>
+#include <platform/pll.h>
+#include <string.h>
+
+#define TRNG_CTRL_REG            (TRNG_BASE+0x00)
+#define TRNG_DATA_REG            (TRNG_BASE+0x08)
+#define TRNG_CONF_REG            (TRNG_BASE+0x0C)
+
+#define TRNG_PDN_VALUE          0x1
+
+/* TRNG_CTRL_REG */
+#define TRNG_RDY         (0x80000000)
+#define TRNG_START       (0x00000001)
+
+/* Assume clock setting for trng is on */
+s32 trng_drv_get_random_data(u8 *buf, u32 len)
+{
+    s32 retval = 0;
+
+    if (0 == len)
+        return 0;
+
+    if (NULL == buf) {
+        dprintf(CRITICAL, "[TRNG] Error: input buffer is NULL\n");
+        return -1;
+    }
+
+    if (readl(TRNG_PDN_STATUS) & TRNG_PDN_VALUE) //TRNG clock is off
+        writel(TRNG_PDN_VALUE, TRNG_PDN_CLR);  //ungate TRNG clock
+
+    if (TRNG_START != (readl(TRNG_CTRL_REG) & TRNG_START)) {
+        writel(TRNG_START, TRNG_CTRL_REG); //start TRNG
+        if (TRNG_START != (readl(TRNG_CTRL_REG) & TRNG_START)) {
+            dprintf(CRITICAL, "[TRNG] Error: fail to start TRNG because clock is disabled\n");
+            return -2;
+        }
+    }
+
+    /* clear output buffer */
+    memset(buf, 0, len);
+
+    /* generate random data with default rings */
+    while (len >= sizeof(u32)) {
+        if(TRNG_RDY != (readl(TRNG_CTRL_REG) & TRNG_RDY)) {
+            spin(1);
+            continue;
+        }
+
+        *(u32 *)buf = readl(TRNG_DATA_REG);
+        retval += sizeof(u32);
+        buf += sizeof(u32);
+        len -= sizeof(u32);
+    }
+
+    writel(0x0, TRNG_CTRL_REG);     //stop TRNG
+    writel(TRNG_PDN_VALUE, TRNG_PDN_SET); //gate TRNG clock
+
+    return retval;
+}
diff --git a/src/bsp/lk/platform/mediatek/mt2712/drivers/wdt/mtk_wdt.c b/src/bsp/lk/platform/mediatek/mt2712/drivers/wdt/mtk_wdt.c
new file mode 100644
index 0000000..f71c6d3
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/drivers/wdt/mtk_wdt.c
@@ -0,0 +1,469 @@
+#include <debug.h>
+#include <platform/mtk_wdt.h>
+#include <reg.h>
+#include <trace.h>
+
+#define LOCAL_TRACE 0
+
+#if ENABLE_WDT_MODULE
+
+static bool mtk_wd_CheckNonResetReg2(unsigned int status)
+{
+    u32 reg;
+
+    reg = readl(MTK_WDT_NONRST_REG2) & MTK_WDT_NONRST2_BOOT_MASK;
+    if (reg == status)
+        return true;
+    else
+        return false;
+}
+
+static void mtk_wd_SetNonResetReg2(unsigned int status, bool flag)
+{
+    u32 reg;
+
+    reg = readl(MTK_WDT_NONRST_REG2);
+    if (flag)
+        reg |= status;
+    else
+        reg &= ~status;
+
+    writel(reg, MTK_WDT_NONRST_REG2);
+}
+
+void set_clr_fastboot_mode(bool flag)
+{
+    mtk_wd_SetNonResetReg2(MTK_WDT_NONRST2_BOOT_BOOTLOADER, flag);
+
+    LTRACEF("set_clr_fastboot_mode\n");
+}
+
+void set_clr_fastbootd_mode(bool flag)
+{
+    mtk_wd_SetNonResetReg2(MTK_WDT_NONRST2_BOOT_FASTBOOTD, flag);
+
+    LTRACEF("set_clr_fastbootd_mode\n");
+}
+
+void set_clr_recovery_mode(bool flag)
+{
+    mtk_wd_SetNonResetReg2(MTK_WDT_NONRST2_BOOT_RECOVERY, flag);
+
+    LTRACEF("set_clr_recovery_mode\n");
+}
+
+void clr_cm4_resume_mode(void)
+{
+    /* clear cm4 resume (bit 21) and show logo (bit 22) flags */
+    mtk_wd_SetNonResetReg2((1U << MTK_WDT_NONRST2_CM4_RESUME), false);
+    mtk_wd_SetNonResetReg2((1U << MTK_WDT_NONRST2_SHOW_LOGO), false);
+}
+
+bool check_fastboot_mode(void)
+{
+    return mtk_wd_CheckNonResetReg2(MTK_WDT_NONRST2_BOOT_BOOTLOADER);
+}
+
+bool check_fastbootd_mode(void)
+{
+    return mtk_wd_CheckNonResetReg2(MTK_WDT_NONRST2_BOOT_FASTBOOTD);
+}
+
+bool check_recovery_mode(void)
+{
+    return mtk_wd_CheckNonResetReg2(MTK_WDT_NONRST2_BOOT_RECOVERY);
+}
+
+void mtk_wdt_disable(void)
+{
+    u32 tmp;
+
+    tmp = readl(MTK_WDT_MODE);
+    tmp &= ~MTK_WDT_MODE_ENABLE;       /* disable watchdog */
+    tmp |= (MTK_WDT_MODE_KEY);         /* need key then write is allowed */
+    writel(tmp, MTK_WDT_MODE);
+}
+
+static void mtk_wdt_reset(char mode)
+{
+    /* Watchdog Rest */
+    unsigned int wdt_mode_val;
+    writel(MTK_WDT_RESTART_KEY, MTK_WDT_RESTART);
+
+    wdt_mode_val = readl(MTK_WDT_MODE);
+    /* clear autorestart bit:
+     * autoretart: 1, bypass power key, 0: not bypass power key */
+    wdt_mode_val &=(~MTK_WDT_MODE_AUTO_RESTART);
+    /* make sure WDT mode is hw reboot mode, can not config isr mode  */
+    wdt_mode_val &= \
+        ~(MTK_WDT_MODE_IRQ | MTK_WDT_MODE_ENABLE | MTK_WDT_MODE_DUAL_MODE);
+
+    wdt_mode_val |= (MTK_WDT_MODE_KEY | MTK_WDT_MODE_EXTEN);
+
+    /* mode != 0 means by pass power key reboot,
+     * We using auto_restart bit as by pass power key flag */
+    if (mode)
+        wdt_mode_val |= MTK_WDT_MODE_AUTO_RESTART;
+
+    writel(wdt_mode_val, MTK_WDT_MODE);
+
+    spin(100);
+    writel(MTK_WDT_SWRST_KEY, MTK_WDT_SWRST);
+}
+
+unsigned int mtk_wdt_check_status(void)
+{
+    static unsigned int status = 0;
+
+    /*
+     * Because WDT_STA register will be cleared after writing WDT_MODE,
+     * we use a static variable to store WDT_STA.
+     * After reset, static varialbe will always be clear to 0,
+     * so only read WDT_STA when static variable is 0 is OK
+     */
+    if (0 == status)
+        status = readl(MTK_WDT_STATUS);
+
+    return status;
+}
+
+static void mtk_wdt_mode_config(bool dual_mode_en,
+                                bool irq,
+                                bool ext_en,
+                                bool ext_pol,
+                                bool wdt_en)
+{
+    unsigned int tmp;
+
+    tmp = readl(MTK_WDT_MODE);
+    tmp |= MTK_WDT_MODE_KEY;
+
+    // Bit 0 : Whether enable watchdog or not
+    if (wdt_en == true)
+        tmp |= MTK_WDT_MODE_ENABLE;
+    else
+        tmp &= ~MTK_WDT_MODE_ENABLE;
+
+    // Bit 1 : Configure extern reset signal polarity.
+    if (ext_pol == true)
+        tmp |= MTK_WDT_MODE_EXT_POL;
+    else
+        tmp &= ~MTK_WDT_MODE_EXT_POL;
+
+    // Bit 2 : Whether enable external reset signal
+    if (ext_en == true)
+        tmp |= MTK_WDT_MODE_EXTEN;
+    else
+        tmp &= ~MTK_WDT_MODE_EXTEN;
+
+    // Bit 3 : Whether generating interrupt instead of reset signal
+    if (irq == true)
+        tmp |= MTK_WDT_MODE_IRQ;
+    else
+        tmp &= ~MTK_WDT_MODE_IRQ;
+
+    // Bit 6 : Whether enable debug module reset
+    if (dual_mode_en == true)
+        tmp |= MTK_WDT_MODE_DUAL_MODE;
+    else
+        tmp &= ~MTK_WDT_MODE_DUAL_MODE;
+
+    /*  Bit 4: WDT_Auto_restart, this is a reserved bit,
+     *  we use it as bypass powerkey flag.
+     *  Because HW reboot always need reboot to kernel, we set it always.
+     */
+    tmp |= MTK_WDT_MODE_AUTO_RESTART;
+
+    writel(tmp, MTK_WDT_MODE);
+    //dual_mode(1); //always dual mode
+    //mdelay(100);
+    LTRACEF("mtk_wdt_mode_config LK mode value=%x", readl(MTK_WDT_MODE));
+}
+
+static void mtk_wdt_set_time_out_value(uint32_t value)
+{
+    static unsigned int timeout;
+
+
+    /* TimeOut = BitField 15:5
+     * Key     = BitField  4:0 = 0x08
+     */
+
+    // sec * 32768 / 512 = sec * 64 = sec * 1 << 6
+    timeout = (unsigned int)(value * ( 1 << 6) );
+    timeout = timeout << 5;
+    writel((timeout | MTK_WDT_LENGTH_KEY), MTK_WDT_LENGTH);
+}
+
+void mtk_wdt_restart(void)
+{
+    // Reset WatchDogTimer's counting value to time out value
+    // ie., keepalive()
+    writel(MTK_WDT_RESTART_KEY, MTK_WDT_RESTART);
+}
+
+static void mtk_wdt_sw_reset(void)
+{
+    printf ("UB WDT SW RESET\n");
+    mtk_wdt_reset(1); /* NOTE here, this reset will cause by pass power key */
+
+    while (1) {
+        printf ("UB SW reset fail ... \n");
+    }
+}
+
+static void mtk_wdt_hw_reset(void)
+{
+    LTRACEF("UB WDT_HW_Reset\n");
+
+    // 1. set WDT timeout 1 secs, 1*64*512/32768 = 1sec
+    mtk_wdt_set_time_out_value(1);
+
+    // 2. enable WDT debug reset enable, generating irq disable,
+    //    ext reset disable, ext reset signal low, wdt enalbe
+    mtk_wdt_mode_config(true, false, false, false, true);
+
+    // 3. reset the watch dog timer to the value set in WDT_LENGTH register
+    mtk_wdt_restart();
+
+    // 4. system will reset
+    while (1);
+}
+
+static const char* parsing_reset_reason(unsigned int wdt_status)
+{
+	const char *rst_reason="normal";
+	switch(wdt_status)
+	{
+		case MTK_WDT_STATUS_HWWDT_RST_WITH_IRQ:
+			rst_reason="hw_rst_with_irq";
+			break;
+
+		case MTK_WDT_STATUS_HWWDT_RST:
+			rst_reason="hw_rst";
+			break;
+
+		case MTK_WDT_STATUS_SWWDT_RST:
+			rst_reason="sw_rst";
+			break;
+
+		case MTK_WDT_STATUS_IRQWDT_RST:
+			rst_reason="irq_rst";
+			break;
+
+		case MTK_WDT_STATUS_SECURITY_RST:
+			rst_reason="security_rst";
+			break;
+
+		case MTK_WDT_STATUS_DEBUGWDT_RST:
+			rst_reason="debug_rst";
+			break;
+
+		case MTK_WDT_STATUS_THERMAL_CTL_RST:
+			rst_reason="thermal_ctl_rst";
+			break;
+
+		case MTK_WDT_STATUS_SPMWDT_RST:
+			rst_reason="spm_rst";
+			break;
+
+		case MTK_WDT_STATUS_SPM_THERMAL_RST:
+			rst_reason="spm_thermal_rst";
+			break;
+
+		default:
+			break;
+	}
+
+	return rst_reason;
+}
+
+void mtk_wdt_init(void)
+{
+    /* This function will store the reset reason: Time out/ SW trigger */
+    dprintf(ALWAYS, "Watchdog Status: %x , boot from %s\n",
+            mtk_wdt_check_status(),
+            parsing_reset_reason(mtk_wdt_check_status()));
+
+    mtk_wdt_mode_config(false, false, false, false, false);
+
+#if (!LK_WDT_DISABLE)
+    mtk_wdt_set_time_out_value(10);
+    mtk_wdt_mode_config(true, true, true, false, true);
+    mtk_wdt_restart();
+#endif
+}
+
+static bool mtk_is_rgu_trigger_reset(void)
+{
+    if (mtk_wdt_check_status())
+        return true;
+    return false;
+}
+
+void mtk_arch_reset(char mode)
+{
+    LTRACEF("UB mtk_arch_reset\n");
+
+    mtk_wdt_reset(mode);
+
+    while (1);
+}
+
+static void rgu_swsys_reset(WD_SYS_RST_TYPE reset_type)
+{
+    if (WD_MD_RST == reset_type) {
+        unsigned int wdt_dbg_ctrl;
+        wdt_dbg_ctrl = readl(MTK_WDT_SWSYSRST);
+        wdt_dbg_ctrl |= MTK_WDT_SWSYS_RST_KEY;
+        wdt_dbg_ctrl |= 0x80;// 1<<7
+        writel(wdt_dbg_ctrl, MTK_WDT_SWSYSRST);
+        spin(1000);
+        wdt_dbg_ctrl = readl(MTK_WDT_SWSYSRST);
+        wdt_dbg_ctrl |= MTK_WDT_SWSYS_RST_KEY;
+        wdt_dbg_ctrl &= (~0x80);// ~(1<<7)
+        writel(wdt_dbg_ctrl, MTK_WDT_SWSYSRST);
+        LTRACEF("rgu pl md reset\n");
+    }
+}
+
+void rgu_dram_reserved(bool enable)
+{
+    volatile unsigned int tmp;
+
+    if (enable) {
+        /* enable ddr reserved mode */
+        tmp = readl(MTK_WDT_MODE);
+        tmp |= (MTK_WDT_MODE_DDR_RESERVE|MTK_WDT_MODE_KEY);
+        writel(tmp, MTK_WDT_MODE);
+    } else {
+        /* disable ddr reserved mode, set reset mode,
+         * disable watchdog output reset signal
+         */
+        tmp = readl(MTK_WDT_MODE);
+        tmp &= (~MTK_WDT_MODE_DDR_RESERVE);
+        tmp |= MTK_WDT_MODE_KEY;
+        writel(tmp, MTK_WDT_MODE);
+    }
+
+    dprintf(CRITICAL,"RGU %s:MTK_WDT_MODE(%x)\n", __func__, tmp);
+}
+
+int rgu_is_reserve_ddr_enabled(void)
+{
+  unsigned int wdt_mode;
+  wdt_mode = readl(MTK_WDT_MODE);
+  if(wdt_mode & MTK_WDT_MODE_DDR_RESERVE)
+  {
+    return 1;
+  }
+  else
+  {
+    return 0;
+  }
+}
+
+int rgu_is_dram_slf(void)
+{
+  unsigned int wdt_dbg_ctrl;
+  wdt_dbg_ctrl = readl(MTK_WDT_DRAMC_CTL);
+  dprintf(CRITICAL,"DDR is in self-refresh. %x\n", wdt_dbg_ctrl);
+  if(wdt_dbg_ctrl & MTK_DDR_SREF_STA)
+  {
+    //dprintf(CRITICAL,"DDR is in self-refresh. %x\n", wdt_dbg_ctrl);
+    return 1;
+  }
+  else
+  {
+    //dprintf(CRITICAL,"DDR is not in self-refresh. %x\n", wdt_dbg_ctrl);
+    return 0;
+  }
+}
+
+void rgu_release_rg_dramc_conf_iso(void)
+{
+  unsigned int wdt_dbg_ctrl;
+  wdt_dbg_ctrl = readl(MTK_WDT_DRAMC_CTL);
+  wdt_dbg_ctrl &= (~MTK_RG_CONF_ISO);
+  wdt_dbg_ctrl |= MTK_DEBUG_CTL_KEY;
+  writel(wdt_dbg_ctrl, MTK_WDT_DRAMC_CTL);
+  dprintf(CRITICAL,"RGU %s:MTK_WDT_DRAMC_CTL(%x)\n", __func__,wdt_dbg_ctrl);
+}
+
+void rgu_release_rg_dramc_iso(void)
+{
+  unsigned int wdt_dbg_ctrl;
+  wdt_dbg_ctrl = readl(MTK_WDT_DRAMC_CTL);
+  wdt_dbg_ctrl &= (~MTK_RG_DRAMC_ISO);
+  wdt_dbg_ctrl |= MTK_DEBUG_CTL_KEY;
+  writel(wdt_dbg_ctrl, MTK_WDT_DRAMC_CTL);
+  dprintf(CRITICAL,"RGU %s:MTK_WDT_DRAMC_CTL(%x)\n", __func__,wdt_dbg_ctrl);
+}
+
+void rgu_release_rg_dramc_sref(void)
+{
+  unsigned int wdt_dbg_ctrl;
+  wdt_dbg_ctrl = readl(MTK_WDT_DRAMC_CTL);
+  wdt_dbg_ctrl &= (~MTK_RG_DRAMC_SREF);
+  wdt_dbg_ctrl |= MTK_DEBUG_CTL_KEY;
+  writel(wdt_dbg_ctrl, MTK_WDT_DRAMC_CTL);
+  dprintf(CRITICAL,"RGU %s:MTK_WDT_DRAMC_CTL(%x)\n", __func__,wdt_dbg_ctrl);
+}
+int rgu_is_reserve_ddr_mode_success(void)
+{
+  unsigned int wdt_dbg_ctrl;
+  wdt_dbg_ctrl = readl(MTK_WDT_DRAMC_CTL);
+  if(wdt_dbg_ctrl & MTK_DDR_RESERVE_RTA)
+  {
+    dprintf(CRITICAL,"WDT DDR reserve mode success! %x\n",wdt_dbg_ctrl);
+    return 1;
+  }
+  else
+  {
+    dprintf(CRITICAL,"WDT DDR reserve mode FAIL! %x\n",wdt_dbg_ctrl);
+    return 0;
+  }
+}
+
+#else
+
+void mtk_wdt_init(void)
+{
+    LTRACEF("UB WDT Dummy init called\n");
+}
+
+static bool mtk_is_rgu_trigger_reset()
+{
+    LTRACEF("UB Dummy mtk_is_rgu_trigger_reset called\n");
+    return FALSE;
+}
+
+void mtk_arch_reset(char mode)
+{
+    LTRACEF("UB WDT Dummy arch reset called\n");
+}
+
+void mtk_wdt_disable(void)
+{
+    LTRACEF("UB WDT Dummy mtk_wdt_disable called\n");
+}
+
+static void mtk_wdt_restart(void)
+{
+    LTRACEF("UB WDT Dummy mtk_wdt_restart called\n");
+}
+static void mtk_wdt_sw_reset(void)
+{
+    LTRACEF("UB WDT Dummy mtk_wdt_sw_reset called\n");
+}
+
+static void mtk_wdt_hw_reset(void)
+{
+    LTRACEF("UB WDT Dummy mtk_wdt_hw_reset called\n");
+}
+
+static void rgu_swsys_reset(WD_SYS_RST_TYPE reset_type)
+{
+    LTRACEF("UB WDT Dummy rgu_swsys_reset called\n");
+}
+#endif
diff --git a/src/bsp/lk/platform/mediatek/mt2712/fixup/android_fixup.c b/src/bsp/lk/platform/mediatek/mt2712/fixup/android_fixup.c
new file mode 100644
index 0000000..7fed149
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/fixup/android_fixup.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <boot_mode.h>
+#include <lib/bio.h>
+#include <lib/kcmdline.h>
+#include <malloc.h>
+#include <string.h>
+#include <trace.h>
+
+#define LOCAL_TRACE 0
+
+static void normal_mode_fixup(const char *ab_suffix)
+{
+
+#if defined(AB_OTA_UPDATER)
+     kcmdline_append("androidboot.force_normal_boot=1");
+#endif
+
+#if !defined(AVB_VERIFY_KERNEL)
+    bdev_t *bdev;
+    char *part_name;
+    char *part_uuid;
+    const char *suffix = ab_suffix ? : "";
+
+    part_name = (char *)malloc(strlen(ROOTFS_PART_NAME) +
+                               strlen(suffix) + 1);
+    if (!part_name) {
+        LTRACEF("Not enough memory for part_name\n");
+        return;
+    }
+
+    sprintf(part_name, "%s%s", ROOTFS_PART_NAME, suffix);
+
+    bdev = bio_open_by_label(part_name);
+    if (!bdev) {
+        LTRACEF("Partition not exist: %s\n", part_name);
+        goto err;
+    }
+
+    part_uuid = (char *)malloc(strlen("root=PARTUUID=") +
+                               strlen(bdev->unique_uuid) + 1);
+    if (!part_uuid) {
+        LTRACEF("Not enough memory for part_uuid\n");
+        goto err;
+    }
+
+    sprintf(part_uuid, "root=PARTUUID=%s", bdev->unique_uuid);
+    kcmdline_append(part_uuid);
+
+    free(part_uuid);
+err:
+    free(part_name);
+#endif
+}
+
+static void recovery_mode_fixup(void)
+{
+#if defined(AVB_VERIFY_KERNEL)
+    kcmdline_subst("root=/dev/dm-0", "root=/dev/ram");
+#else
+    kcmdline_append("root=/dev/ram");
+#endif
+}
+
+/* android specfic fixup hook function */
+void project_fixup_hook(uint32_t boot_mode, const char *ab_suffix)
+{
+    if ((boot_mode == NORMAL_BOOT) || (boot_mode == FASTBOOT_BOOT))
+        normal_mode_fixup(ab_suffix);
+    else /* RECOVERY_BOOT */
+        recovery_mode_fixup();
+
+}
diff --git a/src/bsp/lk/platform/mediatek/mt2712/fixup/plat_fixup.c b/src/bsp/lk/platform/mediatek/mt2712/fixup/plat_fixup.c
new file mode 100644
index 0000000..c83c573
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/fixup/plat_fixup.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <compiler.h>
+#include <err.h>
+#include <lib/kcmdline.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <string.h>
+#include <trace.h>
+
+#if WITH_SMP
+#include <arch/mp_mediatek.h>
+#endif
+
+#if LOG_STORE_SUPPORT
+#include <lib/log_store.h>
+#endif
+
+#define LOCAL_TRACE 0
+
+extern __WEAK void mrdump_append_cmdline(void);
+
+extern void project_fixup_hook(uint32_t boot_mode, const char *ab_suffix);
+
+#if defined(AB_OTA_UPDATER)
+static void ab_fixup(const char *ab_suffix)
+{
+    int rc __UNUSED;
+    char *suffix_arg;
+
+    if (!ab_suffix)
+        return;
+
+    suffix_arg = (char *)malloc(strlen("androidboot.slot_suffix=") +
+                                strlen(ab_suffix) + 1);
+    if (!suffix_arg) {
+        LTRACEF("Not enough memory for suffix cmdline\n");
+        return;
+    }
+
+    sprintf(suffix_arg, "androidboot.slot_suffix=%s", ab_suffix);
+    rc = kcmdline_append(suffix_arg);
+    assert(rc == NO_ERROR);
+    free(suffix_arg);
+}
+#endif
+
+/* need to do fixup when bl2 or bl33 lk will load kernel image */
+#if (defined(ENABLE_BUILTIN_BL33) && (ENABLE_BUILTIN_BL33 == 1)) || \
+    (defined(LK_AS_BL33) && (LK_AS_BL33 == 1))
+int plat_fixup_init(void)
+{
+#if WITH_SMP
+    /* cpu 4 (0x200) as new boot cpu */
+    plat_mp_bootcpu_handover(4);
+#endif
+
+    return kcmdline_init();
+}
+
+void plat_fixup_append(char *append_str)
+{
+    if (kcmdline_append(append_str) != NO_ERROR) {
+        LTRACEF("append str failed: %s\n", append_str);
+    }
+}
+
+void plat_fixup_hook(uint32_t boot_mode, const char *ab_suffix,
+                     void *dtb, size_t dtb_size)
+{
+    /* feature related fixup */
+#if defined(AB_OTA_UPDATER)
+    ab_fixup(ab_suffix);
+#endif
+
+#if (LOG_STORE_SUPPORT == 1)
+    log_store_append_cmdline();
+#endif
+
+    if (mrdump_append_cmdline)
+        mrdump_append_cmdline();
+
+    /* android or yocto project specific fixup */
+    project_fixup_hook(boot_mode, ab_suffix);
+
+    /* finalized fixup */
+    if (kcmdline_finalized(dtb, dtb_size)) {
+        LTRACEF("kcmdline finalized failed.\n");
+    }
+
+#if WITH_SMP
+    /* [TODO] When kernel supports big core booting, remove this to
+     * boot kernel by big core to reduce boot time */
+    plat_mp_bootcpu_handover(0);
+#endif
+}
+#endif
diff --git a/src/bsp/lk/platform/mediatek/mt2712/fixup/yocto_fixup.c b/src/bsp/lk/platform/mediatek/mt2712/fixup/yocto_fixup.c
new file mode 100644
index 0000000..bb02a18
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/fixup/yocto_fixup.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <boot_mode.h>
+#include <sys/types.h>
+
+void project_fixup_hook(uint32_t boot_mode, const char *ab_suffix)
+{
+    /* add yocto specific fixup imp. here */
+}
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/dcm.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/dcm.h
new file mode 100644
index 0000000..b6262e7
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/dcm.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef DCM_H
+#define DCM_H
+
+void mt_dcm_init(void);
+#endif
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/generic_ioctl.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/generic_ioctl.h
new file mode 100644
index 0000000..84c2ec8
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/generic_ioctl.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ***   To edit the content of this header, modify the corresponding
+ ***   source file (e.g. under external/kernel-headers/original/) then
+ ***   run bionic/libc/kernel/tools/update_all.py
+ ***
+ ***   Any manual change here will be lost the next time this script will
+ ***   be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _UAPI_ASM_GENERIC_IOCTL_H
+#define _UAPI_ASM_GENERIC_IOCTL_H
+#define _IOC_NRBITS 8
+#define _IOC_TYPEBITS 8
+#ifndef _IOC_SIZEBITS
+#define _IOC_SIZEBITS 14
+#endif
+#ifndef _IOC_DIRBITS
+#define _IOC_DIRBITS 2
+#endif
+#define _IOC_NRMASK ((1 << _IOC_NRBITS) - 1)
+#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS) - 1)
+#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS) - 1)
+#define _IOC_DIRMASK ((1 << _IOC_DIRBITS) - 1)
+#define _IOC_NRSHIFT 0
+#define _IOC_TYPESHIFT (_IOC_NRSHIFT + _IOC_NRBITS)
+#define _IOC_SIZESHIFT (_IOC_TYPESHIFT + _IOC_TYPEBITS)
+#define _IOC_DIRSHIFT (_IOC_SIZESHIFT + _IOC_SIZEBITS)
+#ifndef _IOC_NONE
+#define _IOC_NONE 0U
+#endif
+#ifndef _IOC_WRITE
+#define _IOC_WRITE 1U
+#endif
+#ifndef _IOC_READ
+#define _IOC_READ 2U
+#endif
+#define _IOC(dir,type,nr,size) (((dir) << _IOC_DIRSHIFT) | ((type) << _IOC_TYPESHIFT) | ((nr) << _IOC_NRSHIFT) | ((size) << _IOC_SIZESHIFT))
+#define _IOC_TYPECHECK(t) (sizeof(t))
+#define _IO(type,nr) _IOC(_IOC_NONE, (type), (nr), 0)
+#define _IOR(type,nr,size) _IOC(_IOC_READ, (type), (nr), (_IOC_TYPECHECK(size)))
+#define _IOW(type,nr,size) _IOC(_IOC_WRITE, (type), (nr), (_IOC_TYPECHECK(size)))
+#define _IOWR(type,nr,size) _IOC(_IOC_READ | _IOC_WRITE, (type), (nr), (_IOC_TYPECHECK(size)))
+#define _IOR_BAD(type,nr,size) _IOC(_IOC_READ, (type), (nr), sizeof(size))
+#define _IOW_BAD(type,nr,size) _IOC(_IOC_WRITE, (type), (nr), sizeof(size))
+#define _IOWR_BAD(type,nr,size) _IOC(_IOC_READ | _IOC_WRITE, (type), (nr), sizeof(size))
+#define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
+#define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
+#define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
+#define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
+#define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT)
+#define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT)
+#define IOC_INOUT ((_IOC_WRITE | _IOC_READ) << _IOC_DIRSHIFT)
+#define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT)
+#define IOCSIZE_SHIFT (_IOC_SIZESHIFT)
+#endif
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/gic.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/gic.h
new file mode 100644
index 0000000..133024e
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/gic.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#pragma once
+
+#include <platform/mt2712.h>
+
+#define GICBASE(n)  (PERIPHERAL_BASE_VIRT + 0x510000)
+#define GICD_OFFSET (0x0)
+#define GICC_OFFSET (0x10000)
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/mmc_core.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mmc_core.h
new file mode 100644
index 0000000..550f428
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mmc_core.h
@@ -0,0 +1,666 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#pragma once
+
+#include <stdbool.h>
+#include <sys/types.h>
+#include <kernel/mutex.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MMC_BLOCK_BITS_SHFT             (9)
+#define MMC_BLOCK_SIZE                  (1 << MMC_BLOCK_BITS_SHFT)
+#define MMC_MAX_BLOCK_SIZE              (1 << MMC_BLOCK_BITS_SHFT)
+
+#define SDIO_MAX_FUNCS                  (7)
+
+#define SD_CMD_BIT                      (1 << 7)
+#define SD_CMD_APP_BIT                  (1 << 8)
+#define SD_CMD_AUTO_BIT                 (1 << 9)
+
+/* MMC command numbers */
+#define MMC_CMD_GO_IDLE_STATE           (0)              /* bc.   no response */
+#define MMC_CMD_SEND_OP_COND            (1)              /* bcr.  R3          */
+#define MMC_CMD_ALL_SEND_CID            (2)              /* bcr.  R2          */
+#define MMC_CMD_SET_RELATIVE_ADDR       (3)              /* ac.   R1          */
+#define MMC_CMD_SET_DSR                 (4)              /* bc.   no response */
+#define MMC_CMD_SLEEP_AWAKE             (5)              /* ac.   R1b         */
+#define MMC_CMD_SWITCH                  (6)              /* ac.   R1b         */
+#define MMC_CMD_SELECT_CARD             (7)              /* ac.   R1/R1b      */
+#define MMC_CMD_SEND_EXT_CSD            (8)              /* adtc. R1          */
+#define MMC_CMD_SEND_CSD                (9)              /* ac.   R2          */
+#define MMC_CMD_SEND_CID                (10)             /* ac.   R2          */
+#define MMC_CMD_READ_DAT_UNTIL_STOP     (11)             /* adtc. R1          */
+#define MMC_CMD_STOP_TRANSMISSION       (12)             /* ac.   R1/R1b      */
+#define MMC_CMD_SEND_STATUS             (13)             /* ac.   R1          */
+#define MMC_CMD_BUSTEST_R               (14)             /* adtc. R1          */
+#define MMC_CMD_GO_INACTIVE_STATE       (15)             /* ac.   no response */
+#define MMC_CMD_SET_BLOCKLEN            (16)             /* ac.   R1          */
+#define MMC_CMD_READ_SINGLE_BLOCK       (17)             /* adtc. R1          */
+#define MMC_CMD_READ_MULTIPLE_BLOCK     (18)             /* adtc. R1          */
+#define MMC_CMD_BUSTEST_W               (19)             /* adtc. R1          */
+#define MMC_CMD_WRITE_DAT_UNTIL_STOP    (20)             /* adtc. R1          */
+#define MMC_CMD21                       (21)             /* adtc. R1  Sandisk  */
+#define MMC_CMD_SET_BLOCK_COUNT         (23)             /* ac.   R1          */
+#define MMC_CMD_WRITE_BLOCK             (24)             /* adtc. R1          */
+#define MMC_CMD_WRITE_MULTIPLE_BLOCK    (25)             /* adtc. R1          */
+#define MMC_CMD_PROGRAM_CID             (26)             /* adtc. R1          */
+#define MMC_CMD_PROGRAM_CSD             (27)             /* adtc. R1          */
+
+#define MMC_CMD_SET_WRITE_PROT          (28)             /* ac.   R1b         */
+#define MMC_CMD_CLR_WRITE_PROT          (29)             /* ac.   R1b         */
+#define MMC_CMD_SEND_WRITE_PROT         (30)             /* adtc. R1          */
+#define MMC_CMD_SEND_WRITE_PROT_TYPE    (31)             /* adtc. R1          */
+#define MMC_CMD_ERASE_WR_BLK_START      (32)
+#define MMC_CMD_ERASE_WR_BLK_END        (33)
+#define MMC_CMD_ERASE_GROUP_START       (35)             /* ac.   R1          */
+#define MMC_CMD_ERASE_GROUP_END         (36)             /* ac.   R1          */
+#define MMC_CMD_ERASE                   (38)             /* ac.   R1b         */
+#define MMC_CMD_FAST_IO                 (39)             /* ac.   R4          */
+#define MMC_CMD_GO_IRQ_STATE            (40)             /* bcr.  R5          */
+#define MMC_CMD_LOCK_UNLOCK             (42)             /* adtc. R1          */
+#define MMC_CMD50                       (50)             /* adtc. R1 Sandisk */
+#define MMC_CMD_APP_CMD                 (55)             /* ac.   R1          */
+#define MMC_CMD_GEN_CMD                 (56)             /* adtc. R1          */
+
+/* SD Card command numbers */
+#define SD_CMD_SEND_RELATIVE_ADDR       (3 | SD_CMD_BIT)
+#define SD_CMD_SWITCH                   (6 | SD_CMD_BIT)
+#define SD_CMD_SEND_IF_COND             (8 | SD_CMD_BIT)
+#define SD_CMD_VOL_SWITCH               (11 | SD_CMD_BIT)
+#define SD_CMD_SEND_TUNING_BLOCK        (19 | SD_CMD_BIT)
+#define SD_CMD_SPEED_CLASS_CTRL         (20 | SD_CMD_BIT)
+
+#define SD_ACMD_SET_BUSWIDTH            (6  | SD_CMD_APP_BIT)
+#define SD_ACMD_SD_STATUS               (13 | SD_CMD_APP_BIT)
+#define SD_ACMD_SEND_NR_WR_BLOCKS       (22 | SD_CMD_APP_BIT)
+#define SD_ACMD_SET_WR_ERASE_CNT        (23 | SD_CMD_APP_BIT)
+#define SD_ACMD_SEND_OP_COND            (41 | SD_CMD_APP_BIT)
+#define SD_ACMD_SET_CLR_CD              (42 | SD_CMD_APP_BIT)
+#define SD_ACMD_SEND_SCR                (51 | SD_CMD_APP_BIT)
+
+/* SDIO Card command numbers */
+#define SD_IO_SEND_OP_COND              (5 | SD_CMD_BIT) /* bcr. R4           */
+#define SD_IO_RW_DIRECT                 (52 | SD_CMD_BIT)/* ac.  R5           */
+#define SD_IO_RW_EXTENDED               (53 | SD_CMD_BIT)/* adtc. R5          */
+
+/* platform dependent command */
+#define SD_ATOCMD_STOP_TRANSMISSION     (12 | SD_CMD_AUTO_BIT)
+#define SD_ATOCMD_SET_BLOCK_COUNT       (23 | SD_CMD_AUTO_BIT)
+
+#define MMC_VDD_145_150 0x00000001  /* VDD voltage 1.45 - 1.50 */
+#define MMC_VDD_150_155 0x00000002  /* VDD voltage 1.50 - 1.55 */
+#define MMC_VDD_155_160 0x00000004  /* VDD voltage 1.55 - 1.60 */
+#define MMC_VDD_160_165 0x00000008  /* VDD voltage 1.60 - 1.65 */
+#define MMC_VDD_165_170 0x00000010  /* VDD voltage 1.65 - 1.70 */
+#define MMC_VDD_17_18   0x00000020  /* VDD voltage 1.7 - 1.8 */
+#define MMC_VDD_18_19   0x00000040  /* VDD voltage 1.8 - 1.9 */
+#define MMC_VDD_19_20   0x00000080  /* VDD voltage 1.9 - 2.0 */
+#define MMC_VDD_20_21   0x00000100  /* VDD voltage 2.0 ~ 2.1 */
+#define MMC_VDD_21_22   0x00000200  /* VDD voltage 2.1 ~ 2.2 */
+#define MMC_VDD_22_23   0x00000400  /* VDD voltage 2.2 ~ 2.3 */
+#define MMC_VDD_23_24   0x00000800  /* VDD voltage 2.3 ~ 2.4 */
+#define MMC_VDD_24_25   0x00001000  /* VDD voltage 2.4 ~ 2.5 */
+#define MMC_VDD_25_26   0x00002000  /* VDD voltage 2.5 ~ 2.6 */
+#define MMC_VDD_26_27   0x00004000  /* VDD voltage 2.6 ~ 2.7 */
+#define MMC_VDD_27_28   0x00008000  /* VDD voltage 2.7 ~ 2.8 */
+#define MMC_VDD_28_29   0x00010000  /* VDD voltage 2.8 ~ 2.9 */
+#define MMC_VDD_29_30   0x00020000  /* VDD voltage 2.9 ~ 3.0 */
+#define MMC_VDD_30_31   0x00040000  /* VDD voltage 3.0 ~ 3.1 */
+#define MMC_VDD_31_32   0x00080000  /* VDD voltage 3.1 ~ 3.2 */
+#define MMC_VDD_32_33   0x00100000  /* VDD voltage 3.2 ~ 3.3 */
+#define MMC_VDD_33_34   0x00200000  /* VDD voltage 3.3 ~ 3.4 */
+#define MMC_VDD_34_35   0x00400000  /* VDD voltage 3.4 ~ 3.5 */
+#define MMC_VDD_35_36   0x00800000  /* VDD voltage 3.5 ~ 3.6 */
+#define MMC_CARD_BUSY   0x80000000  /* Card Power up status bit */
+#define MMC_VDD_165_195     0x00000080  /* VDD voltage 1.65 - 1.95 */ //Add this line by reference to include/linux/mmc/host.h in linux kernel
+#define MMC_VDD_27_36       0x00FF8000
+
+#define EMMC_VER_50   (50)
+#define EMMC_VER_45   (45)
+#define EMMC_VER_44   (44)
+#define EMMC_VER_43   (43)
+#define EMMC_VER_42   (42)
+#define SD_VER_10     (10)
+#define SD_VER_20     (20)
+#define SD_VER_30     (30)
+
+#define MMC_ERR_NONE          0
+#define MMC_ERR_TIMEOUT       1
+#define MMC_ERR_BADCRC        2
+#define MMC_ERR_FIFO          3
+#define MMC_ERR_FAILED        4
+#define MMC_ERR_INVALID       5
+#define MMC_ERR_CMDTUNEFAIL   6
+#define MMC_ERR_READTUNEFAIL  7
+#define MMC_ERR_WRITETUNEFAIL 8
+#define MMC_ERR_CMD_TIMEOUT   9
+#define MMC_ERR_CMD_RSPCRC    10
+#define MMC_ERR_ACMD_TIMEOUT  11
+#define MMC_ERR_ACMD_RSPCRC   12
+#define MMC_ERR_AXI_RSPCRC    13
+#define MMC_ERR_UNEXPECT      14
+#define MMC_ERR_RETRY         15
+
+#define MMC_POWER_OFF       0
+#define MMC_POWER_UP        1
+#define MMC_POWER_ON        2
+
+#define MMC_BUS_WIDTH_1     0
+#define MMC_BUS_WIDTH_4     2
+
+#define SD_BUS_WIDTH_1      0
+#define SD_BUS_WIDTH_4      2
+
+#define MMC_STATE_PRESENT       (1<<0)      /* present in sysfs */
+#define MMC_STATE_READONLY      (1<<1)      /* card is read-only */
+#define MMC_STATE_HIGHSPEED     (1<<2)      /* card is in high speed mode */
+#define MMC_STATE_BLOCKADDR     (1<<3)      /* card uses block-addressing */
+#define MMC_STATE_HIGHCAPS      (1<<4)
+#define MMC_STATE_UHS1          (1<<5)      /* card is in ultra high speed mode */
+#define MMC_STATE_DDR           (1<<6)      /* card is in ddr mode */
+#define MMC_STATE_HS200         (1<<7)
+#define MMC_STATE_HS400         (1<<8)
+#define MMC_STATE_BACKYARD      (1<<9)
+
+#define R1_OUT_OF_RANGE         (1UL << 31) /* er, c */
+#define R1_ADDRESS_ERROR        (1 << 30)   /* erx, c */
+#define R1_BLOCK_LEN_ERROR      (1 << 29)   /* er, c */
+#define R1_ERASE_SEQ_ERROR      (1 << 28)   /* er, c */
+#define R1_ERASE_PARAM          (1 << 27)   /* ex, c */
+#define R1_WP_VIOLATION         (1 << 26)   /* erx, c */
+#define R1_CARD_IS_LOCKED       (1 << 25)   /* sx, a */
+#define R1_LOCK_UNLOCK_FAILED   (1 << 24)   /* erx, c */
+#define R1_COM_CRC_ERROR        (1 << 23)   /* er, b */
+#define R1_ILLEGAL_COMMAND      (1 << 22)   /* er, b */
+#define R1_CARD_ECC_FAILED      (1 << 21)   /* ex, c */
+#define R1_CC_ERROR             (1 << 20)   /* erx, c */
+#define R1_ERROR                (1 << 19)   /* erx, c */
+#define R1_UNDERRUN             (1 << 18)   /* ex, c */
+#define R1_OVERRUN              (1 << 17)   /* ex, c */
+#define R1_CID_CSD_OVERWRITE    (1 << 16)   /* erx, c, CID/CSD overwrite */
+#define R1_WP_ERASE_SKIP        (1 << 15)   /* sx, c */
+#define R1_CARD_ECC_DISABLED    (1 << 14)   /* sx, a */
+#define R1_ERASE_RESET          (1 << 13)   /* sr, c */
+#define R1_STATUS(x)            (x & 0xFFFFE000)
+#define R1_CURRENT_STATE(x)     ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */
+#define R1_READY_FOR_DATA       (1 << 8)    /* sx, a */
+#define R1_SWITCH_ERROR         (1 << 7)    /* ex, b */
+#define R1_URGENT_BKOPS         (1 << 6)    /* sr, a */
+#define R1_APP_CMD              (1 << 5)    /* sr, c */
+
+/*
+ * Card Command Classes (CCC)
+ */
+#define CCC_BASIC               (1<<0)  /* (0) Basic protocol functions */
+/* (CMD0,1,2,3,4,7,9,10,12,13,15) */
+#define CCC_STREAM_READ         (1<<1)  /* (1) Stream read commands */
+/* (CMD11) */
+#define CCC_BLOCK_READ          (1<<2)  /* (2) Block read commands */
+/* (CMD16,17,18) */
+#define CCC_STREAM_WRITE        (1<<3)  /* (3) Stream write commands */
+/* (CMD20) */
+#define CCC_BLOCK_WRITE         (1<<4)  /* (4) Block write commands */
+/* (CMD16,24,25,26,27) */
+#define CCC_ERASE               (1<<5)  /* (5) Ability to erase blocks */
+/* (CMD32,33,34,35,36,37,38,39) */
+#define CCC_WRITE_PROT          (1<<6)  /* (6) Able to write protect blocks */
+/* (CMD28,29,30) */
+#define CCC_LOCK_CARD           (1<<7)  /* (7) Able to lock down card */
+/* (CMD16,CMD42) */
+#define CCC_APP_SPEC            (1<<8)  /* (8) Application specific */
+/* (CMD55,56,57,ACMD*) */
+#define CCC_IO_MODE             (1<<9)  /* (9) I/O mode */
+/* (CMD5,39,40,52,53) */
+#define CCC_SWITCH              (1<<10) /* (10) High speed switch */
+/* (CMD6,34,35,36,37,50) */
+/* (11) Reserved */
+/* (CMD?) */
+
+/*
+ * CSD field definitions
+ */
+
+#define CSD_STRUCT_VER_1_0  0   /* Valid for system specification 1.0 - 1.2 */
+#define CSD_STRUCT_VER_1_1  1   /* Valid for system specification 1.4 - 2.2 */
+#define CSD_STRUCT_VER_1_2  2   /* Valid for system specification 3.1 - 3.2 - 3.31 - 4.0 - 4.1 */
+#define CSD_STRUCT_EXT_CSD  3   /* Version is coded in CSD_STRUCTURE in EXT_CSD */
+
+#define CSD_SPEC_VER_0      0   /* Implements system specification 1.0 - 1.2 */
+#define CSD_SPEC_VER_1      1   /* Implements system specification 1.4 */
+#define CSD_SPEC_VER_2      2   /* Implements system specification 2.0 - 2.2 */
+#define CSD_SPEC_VER_3      3   /* Implements system specification 3.1 - 3.2 - 3.31 */
+#define CSD_SPEC_VER_4      4   /* Implements system specification 4.0 - 4.1 */
+
+/*
+ * EXT_CSD fields
+ */
+
+#define EXT_CSD_BADBLK_MGMT             134 /* R/W */
+#define EXT_CSD_ENH_START_ADDR          136 /* R/W 4 bytes */
+#define EXT_CSD_ENH_SIZE_MULT           140 /* R/W 3 bytes */
+#define EXT_CSD_GP1_SIZE_MULT           143 /* R/W 3 bytes */
+#define EXT_CSD_GP2_SIZE_MULT           146 /* R/W 3 bytes */
+#define EXT_CSD_GP3_SIZE_MULT           149 /* R/W 3 bytes */
+#define EXT_CSD_GP4_SIZE_MULT           152 /* R/W 3 bytes */
+#define EXT_CSD_PART_SET_COMPL          155 /* R/W */
+#define EXT_CSD_PART_ATTR               156 /* R/W 3 bytes */
+#define EXT_CSD_MAX_ENH_SIZE_MULT       157 /* R/W 3 bytes */
+#define EXT_CSD_PART_SUPPORT            160 /* R */
+#define EXT_CSD_HPI_MGMT                161 /* R/W/E_P (4.41) */
+#define EXT_CSD_RST_N_FUNC              162 /* R/W */
+#define EXT_CSD_BKOPS_EN                163 /* R/W (4.41) */
+#define EXT_CSD_BKOPS_START             164 /* W/E_P (4.41) */
+#define EXT_CSD_WR_REL_PARAM            166 /* R (4.41) */
+#define EXT_CSD_WR_REL_SET              167 /* R/W (4.41) */
+#define EXT_CSD_RPMB_SIZE_MULT          168 /* R */
+#define EXT_CSD_FW_CONFIG               169 /* R/W */
+#define EXT_CSD_USR_WP                  171 /* R/W, R/W/C_P & R/W/E_P */
+#define EXT_CSD_BOOT_WP                 173 /* R/W, R/W/C_P */
+#define EXT_CSD_ERASE_GRP_DEF           175 /* R/W/E */
+#define EXT_CSD_BOOT_BUS_WIDTH          177 /* R/W/E */
+#define EXT_CSD_BOOT_CONFIG_PROT        178 /* R/W & R/W/C_P */
+#define EXT_CSD_PART_CFG                179 /* R/W/E & R/W/E_P */
+#define EXT_CSD_ERASED_MEM_CONT         181 /* R */
+#define EXT_CSD_BUS_WIDTH               183 /* R/W */
+#define EXT_CSD_HS_TIMING               185 /* R/W */
+#define EXT_CSD_PWR_CLASS               187 /* R/W/E_P */
+#define EXT_CSD_CMD_SET_REV             189 /* R */
+#define EXT_CSD_CMD_SET                 191 /* R/W/E_P */
+#define EXT_CSD_REV                     192 /* R */
+#define EXT_CSD_STRUCT                  194 /* R */
+#define EXT_CSD_CARD_TYPE               196 /* RO */
+#define EXT_CSD_OUT_OF_INTR_TIME        198 /* R (4.41) */
+#define EXT_CSD_PART_SWITCH_TIME        199 /* R (4.41) */
+#define EXT_CSD_PWR_CL_52_195           200 /* R */
+#define EXT_CSD_PWR_CL_26_195           201 /* R */
+#define EXT_CSD_PWR_CL_52_360           202 /* R */
+#define EXT_CSD_PWR_CL_26_360           203 /* R */
+#define EXT_CSD_MIN_PERF_R_4_26         205 /* R */
+#define EXT_CSD_MIN_PERF_W_4_26         206 /* R */
+#define EXT_CSD_MIN_PERF_R_8_26_4_25    207 /* R */
+#define EXT_CSD_MIN_PERF_W_8_26_4_25    208 /* R */
+#define EXT_CSD_MIN_PERF_R_8_52         209 /* R */
+#define EXT_CSD_MIN_PERF_W_8_52         210 /* R */
+#define EXT_CSD_SEC_CNT                 212 /* RO, 4 bytes */
+#define EXT_CSD_S_A_TIMEOUT             217 /* R */
+#define EXT_CSD_S_C_VCCQ                219 /* R */
+#define EXT_CSD_S_C_VCC                 220 /* R */
+#define EXT_CSD_HC_WP_GPR_SIZE          221 /* R */
+#define EXT_CSD_REL_WR_SEC_C            222 /* R */
+#define EXT_CSD_ERASE_TIMEOUT_MULT      223 /* R */
+#define EXT_CSD_HC_ERASE_GRP_SIZE       224 /* R */
+#define EXT_CSD_ACC_SIZE                225 /* R */
+#define EXT_CSD_BOOT_SIZE_MULT          226 /* R */
+#define EXT_CSD_BOOT_INFO               228 /* R */
+#define EXT_CSD_SEC_TRIM_MULT           229 /* R */
+#define EXT_CSD_SEC_ERASE_MULT          230 /* R */
+#define EXT_CSD_SEC_FEATURE_SUPPORT     231 /* R */
+#define EXT_CSD_TRIM_MULT               232 /* R */
+#define EXT_CSD_MIN_PERF_DDR_R_8_52     234 /* R */
+#define EXT_CSD_MIN_PERF_DDR_W_8_52     235 /* R */
+#define EXT_CSD_PWR_CL_DDR_52_195       238 /* R */
+#define EXT_CSD_PWR_CL_DDR_52_360       239 /* R */
+#define EXT_CSD_INI_TIMEOUT_AP          241 /* R */
+#define EXT_CSD_CORRECT_PRG_SECTS_NUM   242 /* R, 4 bytes (4.41) */
+#define EXT_CSD_BKOPS_STATUS            246 /* R (4.41) */
+#define EXT_CSD_BKOPS_SUPP              502 /* R (4.41) */
+#define EXT_CSD_HPI_FEATURE             503 /* R (4.41) */
+#define EXT_CSD_S_CMD_SET               504 /* R */
+
+/*
+ * EXT_CSD field definitions
+ */
+
+/* SEC_FEATURE_SUPPORT[231] */
+#define EXT_CSD_SEC_FEATURE_ER_EN       (1<<0)
+#define EXT_CSD_SEC_FEATURE_BD_BLK_EN   (1<<2)
+#define EXT_CSD_SEC_FEATURE_GB_CL_EN    (1<<4)
+
+/* BOOT_INFO[228] */
+#define EXT_CSD_BOOT_INFO_ALT_BOOT      (1<<0)
+#define EXT_CSD_BOOT_INFO_DDR_BOOT      (1<<1)
+#define EXT_CSD_BOOT_INFO_HS_BOOT       (1<<2)
+
+#define EXT_CSD_CMD_SET_NORMAL          (1<<0)
+#define EXT_CSD_CMD_SET_SECURE          (1<<1)
+#define EXT_CSD_CMD_SET_CPSECURE        (1<<2)
+
+#define EXT_CSD_CARD_TYPE_26            (1<<0)  /* Card can run at 26MHz */
+#define EXT_CSD_CARD_TYPE_52            (1<<1)  /* Card can run at 52MHz */
+#define EXT_CSD_CARD_TYPE_DDR_52        (1<<2)  /* Card can run at DDR 52MHz@1.8V or 3V */
+#define EXT_CSD_CARD_TYPE_DDR_52_1_2V   (1<<3)  /* Card can run at DDR 52MHz@1.2V */
+#define EXT_CSD_CARD_TYPE_HS200_1_8V    (1<<4)  /* Card can run at 200MHz@ 1.8V*/
+#define EXT_CSD_CARD_TYPE_HS200_1_2V    (1<<5)  /* Card can run at 200MHz@ 1.2V*/
+#define EXT_CSD_CARD_TYPE_HS400_1_8V    (1<<6)  /* Card can run at 200MHz@ 1.8V*/
+#define EXT_CSD_CARD_TYPE_HS400_1_2V    (1<<7)  /* Card can run at 200MHz@ 1.2V*/
+
+/* BUS_WIDTH[183] */
+#define EXT_CSD_BUS_WIDTH_1             (0) /* Card is in 1 bit mode */
+#define EXT_CSD_BUS_WIDTH_4             (1) /* Card is in 4 bit mode */
+#define EXT_CSD_BUS_WIDTH_8             (2) /* Card is in 8 bit mode */
+#define EXT_CSD_BUS_WIDTH_4_DDR         (5) /* Card is in 4 bit mode + DDR */
+#define EXT_CSD_BUS_WIDTH_8_DDR         (6) /* Card is in 8 bit mode + DDR */
+
+/* high speed timing */
+#define EXT_CSD_HS_TIMEING_BACKWARDS    (0) /* selecting backwards compatibility interface timing */
+#define EXT_CSD_HS_TIMEING_HS           (1) /* selecting high speed  interface timing */
+#define EXT_CSD_HS_TIMEING_HS200        (2) /* selecting hs200 interface timing */
+#define EXT_CSD_HS_TIMEING_HS400        (3) /* selecting hs400 interface timing */
+
+/* ERASED_MEM_CONT[181] */
+#define EXT_CSD_ERASED_MEM_CONT_0       (0)
+#define EXT_CSD_ERASED_MEM_CONT_1       (1)
+
+/* PARTITION CONFIG[179] */
+#define EXT_CSD_PART_CFG_DEFT_PART      (0)
+#define EXT_CSD_PART_CFG_BOOT_PART_1    (1)
+#define EXT_CSD_PART_CFG_BOOT_PART_2    (2)
+#define EXT_CSD_PART_CFG_RPMB_PART      (3)
+#define EXT_CSD_PART_CFG_GP_PART_1      (4)
+#define EXT_CSD_PART_CFG_GP_PART_2      (5)
+#define EXT_CSD_PART_CFG_GP_PART_3      (6)
+#define EXT_CSD_PART_CFG_GP_PART_4      (7)
+#define EXT_CSD_PART_CFG_EN_NO_BOOT     (0 << 3)
+#define EXT_CSD_PART_CFG_EN_BOOT_PART_1 (1 << 3)
+#define EXT_CSD_PART_CFG_EN_BOOT_PART_2 (2 << 3)
+#define EXT_CSD_PART_CFG_EN_USER_AREA   (7 << 3)
+#define EXT_CSD_PART_CFG_EN_NO_ACK      (0 << 6)
+#define EXT_CSD_PART_CFG_EN_ACK         (1 << 6)
+
+/* BOOT_CONFIG_PROT[178] */
+#define EXT_CSD_EN_PWR_BOOT_CFG_PROT    (1)
+#define EXT_CSD_EN_PERM_BOOT_CFG_PROT   (1<<4)  /* Carefully */
+
+/* BOOT_BUS_WIDTH[177] */
+#define EXT_CSD_BOOT_BUS_WIDTH_1        (0)
+#define EXT_CSD_BOOT_BUS_WIDTH_4        (1)
+#define EXT_CSD_BOOT_BUS_WIDTH_8        (2)
+#define EXT_CSD_BOOT_BUS_RESET          (1 << 2)
+
+#define EXT_CSD_BOOT_BUS_MODE_DEFT      (0 << 3)
+#define EXT_CSD_BOOT_BUS_MODE_HS        (1 << 3)
+#define EXT_CSD_BOOT_BUS_MODE_DDR       (2 << 3)
+
+/* ERASE_GROUP_DEF[175] */
+#define EXT_CSD_ERASE_GRP_DEF_EN        (1)
+
+/* BOOT_WP[173] */
+#define EXT_CSD_BOOT_WP_EN_PWR_WP       (1)
+#define EXT_CSD_BOOT_WP_EN_PERM_WP      (1 << 2)
+#define EXT_CSD_BOOT_WP_DIS_PERM_WP     (1 << 4)
+#define EXT_CSD_BOOT_WP_DIS_PWR_WP      (1 << 6)
+
+/* USER_WP[171] */
+#define EXT_CSD_USR_WP_EN_PWR_WP        (1)
+#define EXT_CSD_USR_WP_EN_PERM_WP       (1<<2)
+#define EXT_CSD_USR_WP_DIS_PWR_WP       (1<<3)
+#define EXT_CSD_USR_WP_DIS_PERM_WP      (1<<4)
+#define EXT_CSD_USR_WP_DIS_CD_PERM_WP   (1<<6)
+#define EXT_CSD_USR_WP_DIS_PERM_PWD     (1<<7)
+
+/* RST_n_FUNCTION[162] */
+#define EXT_CSD_RST_N_TEMP_DIS          (0)
+#define EXT_CSD_RST_N_PERM_EN           (1) /* carefully */
+#define EXT_CSD_RST_N_PERM_DIS          (2) /* carefully */
+
+/* PARTITIONING_SUPPORT[160] */
+#define EXT_CSD_PART_SUPPORT_PART_EN     (1)
+#define EXT_CSD_PART_SUPPORT_ENH_ATTR_EN (1<<1)
+
+/* PARTITIONS_ATTRIBUTE[156] */
+#define EXT_CSD_PART_ATTR_ENH_USR       (1<<0)
+#define EXT_CSD_PART_ATTR_ENH_1         (1<<1)
+#define EXT_CSD_PART_ATTR_ENH_2         (1<<2)
+#define EXT_CSD_PART_ATTR_ENH_3         (1<<3)
+#define EXT_CSD_PART_ATTR_ENH_4         (1<<4)
+
+/* PARTITION_SETTING_COMPLETED[156] */
+#define EXT_CSD_PART_SET_COMPL_BIT      (1<<0)
+
+/*
+ * MMC_SWITCH access modes
+ */
+
+#define MMC_SWITCH_MODE_CMD_SET     0x00    /* Change the command set */
+#define MMC_SWITCH_MODE_SET_BITS    0x01    /* Set bits which are 1 in value */
+#define MMC_SWITCH_MODE_CLEAR_BITS  0x02    /* Clear bits which are 1 in value */
+#define MMC_SWITCH_MODE_WRITE_BYTE  0x03    /* Set target to value */
+
+#define MMC_SWITCH_MODE_SDR12       0
+#define MMC_SWITCH_MODE_SDR25       1
+#define MMC_SWITCH_MODE_SDR50       2
+#define MMC_SWITCH_MODE_SDR104      3
+#define MMC_SWITCH_MODE_DDR50       4
+
+#define MMC_SWITCH_MODE_DRV_TYPE_B  0
+#define MMC_SWITCH_MODE_DRV_TYPE_A  1
+#define MMC_SWITCH_MODE_DRV_TYPE_C  2
+#define MMC_SWITCH_MODE_DRV_TYPE_D  3
+
+#define MMC_SWITCH_MODE_CL_200MA    0
+#define MMC_SWITCH_MODE_CL_400MA    1
+#define MMC_SWITCH_MODE_CL_600MA    2
+#define MMC_SWITCH_MODE_CL_800MA    3
+
+/*
+ * MMC_ERASE arguments
+ */
+#define MMC_ERASE_SECURE_REQ        (1 << 31)
+#define MMC_ERASE_GC_REQ            (1 << 15)
+#define MMC_ERASE_DISCARD           (3 << 0)
+#define MMC_ERASE_TRIM              (1 << 0)
+#define MMC_ERASE_NORMAL            (0)
+
+#define HOST_BUS_WIDTH_1            (1)
+#define HOST_BUS_WIDTH_4            (4)
+#define HOST_BUS_WIDTH_8            (8)
+
+#define EMMC_BOOT_PULL_CMD_MODE     (0)
+#define EMMC_BOOT_RST_CMD_MODE      (1)
+
+enum {
+    EMMC_BOOT_PWR_RESET = 0,
+    EMMC_BOOT_RST_N_SIG,
+    EMMC_BOOT_PRE_IDLE_CMD
+};
+
+enum {
+    EMMC_PART_UNKNOWN = 0,
+    EMMC_PART_BOOT1,
+    EMMC_PART_BOOT2,
+    EMMC_PART_USER = 7
+};
+
+enum {
+    RESP_NONE = 0,
+    RESP_R1,
+    RESP_R2,
+    RESP_R3,
+    RESP_R4,
+    RESP_R5,
+    RESP_R6,
+    RESP_R7,
+    RESP_R1B
+};
+
+struct mmc_csd {
+    unsigned char  csd_struct;
+    unsigned char  mmca_vsn;
+    unsigned int   max_dtr;             /* max. data transfer rate */
+    unsigned int   read_blkbits;        /* max. read data block length */
+    unsigned int   capacity;            /* card capacity */
+};
+
+struct mmc_ext_csd {
+    unsigned int    sectors;
+    unsigned int    hs_max_dtr;
+    unsigned char   rev;
+    unsigned char   boot_info;
+    unsigned int    boot_part_sz;
+    unsigned int    rpmb_sz;
+    unsigned char   ddr_support;
+    unsigned char   hs400_support;
+    unsigned char   hs_timing;
+    unsigned char   part_cfg;
+    unsigned char   sec_support;
+    unsigned char   reset_en;
+};
+
+#define MMC_CAP_4_BIT_DATA      (1 << 0) /* Can the host do 4 bit transfers */
+#define MMC_CAP_MULTIWRITE      (1 << 1) /* Can accurately report bytes sent to card on error */
+#define MMC_CAP_BYTEBLOCK       (1 << 2) /* Can do non-log2 block sizes */
+#define MMC_CAP_MMC_HIGHSPEED   (1 << 3) /* Can do MMC high-speed timing */
+#define MMC_CAP_SD_HIGHSPEED    (1 << 4) /* Can do SD high-speed timing */
+#define MMC_CAP_8_BIT_DATA      (1 << 5) /* Can the host do 8 bit transfers */
+#define MMC_CAP_SD_UHS1         (1 << 6) /* Can do SD ultra-high-speed timing */
+#define MMC_CAP_DDR             (1 << 7) /* The host support dual data rate */
+#define MMC_CAP_EMMC_HS200      (1 << 8) /* The host support dual data rate */
+#define MMC_CAP_EMMC_HS400      (1 << 9) /* The host support dual data rate */
+
+struct mmc_host {
+    u8 host_id;
+    struct mmc_card *card;
+    u32 max_phys_segs;
+    addr_t base;      /* host base address */
+    u32 caps;         /* Host capabilities */
+    u32 f_min;        /* host min. frequency */
+    u32 f_max;        /* host max. frequency */
+    u32 clk;          /* host clock speed */
+    u32 sclk;         /* SD/MS clock speed */
+    u32 blklen;       /* block len */
+    u32 ocr;          /* current ocr */
+    u32 ocr_avail;    /* available ocr */
+    u32 timeout_ns;   /* data timeout ns */
+    u32 timeout_clks; /* data timeout clks */
+    u8  clksrc;       /* clock source */
+    u8  hclksrc;       /* clock source */
+    u8  curr_part;    /* host current working partition */
+    u32 intr_mask;    /* Interrupt mask */
+    u32  time_read;
+    void *priv;       /* private data */
+    mutex_t lock;    /* mutex lock for multi-thread */
+    int (*blk_read)(struct mmc_host *host, u8 *dst, u32 src, u32 nblks);
+    int (*blk_write)(struct mmc_host *host, u32 dst, u8 *src, u32 nblks);
+    bool skip_reinit;
+};
+
+#define MMC_TYPE_UNKNOWN    (0)          /* Unknown card */
+#define MMC_TYPE_MMC        (0x00000001) /* MMC card */
+#define MMC_TYPE_SD         (0x00000002) /* SD card */
+#define MMC_TYPE_SDIO       (0x00000004) /* SDIO card */
+
+/* MMC device */
+struct mmc_card {
+    struct mmc_host        *host;       /* the host this device belongs to */
+    unsigned int            nblks;
+    unsigned int            blklen;
+    unsigned int            ocr;
+    unsigned int            maxhz;
+    unsigned int            uhs_mode;
+    unsigned int            rca;        /* relative card address of device */
+    unsigned int            type;       /* card type */
+    unsigned int            sdio_funcs; /* number of SDIO functions */
+    unsigned short          state;      /* (our) card state */
+    unsigned short          ready;      /* card is ready or not */
+    u32                     raw_cid[4]; /* raw card CID */
+    u32                     raw_csd[4]; /* raw card CSD */
+    struct mmc_csd          csd;        /* card specific */
+    struct mmc_ext_csd      ext_csd;    /* mmc v4 extended card specific */
+    u8                      version;    /* the SD card version, 1.0, 2.0, or 3.0*/
+};
+
+struct mmc_command {
+    u32 opcode;
+    u32 arg;
+    u32 rsptyp;
+    u32 resp[4];
+    u32 timeout;
+    u32 retries;    /* max number of retries */
+    u32 error;      /* command error */
+};
+
+struct mmc_data {
+    u8  *buf;
+    struct mmc_command *cmd;
+    u32  blks;
+    u32  timeout;   /* ms */
+};
+
+#define mmc_card_mmc(c)             ((c)->type & MMC_TYPE_MMC)
+#define mmc_card_sd(c)              ((c)->type & MMC_TYPE_SD)
+
+#define mmc_card_set_host(c,h)      ((c)->host = (h))
+#define mmc_card_set_unknown(c)     ((c)->type = MMC_TYPE_UNKNOWN)
+#define mmc_card_set_mmc(c)         ((c)->type |= MMC_TYPE_MMC)
+#define mmc_card_set_sd(c)          ((c)->type |= MMC_TYPE_SD)
+
+#define mmc_card_present(c)         ((c)->state & MMC_STATE_PRESENT)
+#define mmc_card_readonly(c)        ((c)->state & MMC_STATE_READONLY)
+#define mmc_card_backyard(c)        ((c)->state & MMC_STATE_BACKYARD)
+#define mmc_card_highspeed(c)       ((c)->state & MMC_STATE_HIGHSPEED)
+#define mmc_card_uhs1(c)            ((c)->state & MMC_STATE_UHS1)
+#define mmc_card_hs200(c)           ((c)->state & MMC_STATE_HS200)
+#define mmc_card_hs400(c)           ((c)->state & MMC_STATE_HS400)
+#define mmc_card_ddr(c)             ((c)->state & MMC_STATE_DDR)
+#define mmc_card_blockaddr(c)       ((c)->state & MMC_STATE_BLOCKADDR)
+#define mmc_card_highcaps(c)        ((c)->state & MMC_STATE_HIGHCAPS)
+
+#define mmc_card_set_present(c)     ((c)->state |= MMC_STATE_PRESENT)
+#define mmc_card_set_readonly(c)    ((c)->state |= MMC_STATE_READONLY)
+#define mmc_card_set_backyard(c)    ((c)->state |= MMC_STATE_BACKYARD)
+#define mmc_card_set_highspeed(c)   ((c)->state |= MMC_STATE_HIGHSPEED)
+#define mmc_card_set_uhs1(c)        ((c)->state |= MMC_STATE_UHS1)
+#define mmc_card_set_hs200(c)       ((c)->state |= MMC_STATE_HS200)
+#define mmc_card_set_hs400(c)       ((c)->state |= MMC_STATE_HS400)
+#define mmc_card_set_ddr(c)         ((c)->state |= MMC_STATE_DDR)
+#define mmc_card_set_blockaddr(c)   ((c)->state |= MMC_STATE_BLOCKADDR)
+
+#define mmc_card_clear_present(c)     ((c)->state &= ~MMC_STATE_PRESENT)
+#define mmc_card_clear_readonly(c)    ((c)->state &= ~MMC_STATE_READONLY)
+#define mmc_card_clear_highspeed(c)   ((c)->state &= ~MMC_STATE_HIGHSPEED)
+#define mmc_card_clear_uhs1(c)        ((c)->state &= ~MMC_STATE_UHS1)
+#define mmc_card_clear_hs200(c)       ((c)->state &= ~MMC_STATE_HS200)
+#define mmc_card_clear_hs400(c)       ((c)->state &= ~MMC_STATE_HS400)
+#define mmc_card_clear_ddr(c)         ((c)->state &= ~MMC_STATE_DDR)
+#define mmc_card_clear_blockaddr(c)   ((c)->state &= ~MMC_STATE_BLOCKADDR)
+
+#define mmc_card_clr_ddr(c)         ((c)->state &= ~MMC_STATE_DDR)
+#define mmc_card_clr_speed_mode(c)  ((c)->state &= ~(MMC_STATE_HS400 | MMC_STATE_HS200 | MMC_STATE_UHS1 | MMC_STATE_HIGHSPEED | MMC_STATE_BACKYARD))
+
+#define mmc_card_name(c)            ((c)->cid.prod_name)
+
+#define mmc_op_multi(op)    (((op) == MMC_CMD_READ_MULTIPLE_BLOCK) || \
+        ((op) == MMC_CMD_WRITE_MULTIPLE_BLOCK))
+
+int mmc_get_boot_part(u32 *bootpart);
+int mmc_set_boot_part(u32 bootpart);
+struct mmc_card *emmc_init_stage1(bool *retry_opcond, bool skip_reinit);
+int emmc_init_stage2(struct mmc_card *card, bool retry_opcond, bool skip_reinit);
+int sdmmc_init(u8 host_id, bool skip_reinit);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/mmc_ioctl.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mmc_ioctl.h
new file mode 100644
index 0000000..6afd29b
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mmc_ioctl.h
@@ -0,0 +1,76 @@
+#ifndef LINUX_MMC_IOCTL_H
+#define LINUX_MMC_IOCTL_H
+
+#include <sys/types.h>
+#include "generic_ioctl.h"
+
+struct mmc_ioc_cmd {
+	/* Implies direction of data.  true = write, false = read */
+	int write_flag;
+
+	/* Application-specific command.  true = precede with CMD55 */
+	int is_acmd;
+
+	u32 opcode;
+	u32 arg;
+	u32 response[4];  /* CMD response */
+	unsigned int flags;
+	unsigned int blksz;
+	unsigned int blocks;
+
+	/*
+	 * Sleep at least postsleep_min_us useconds, and at most
+	 * postsleep_max_us useconds *after* issuing command.  Needed for
+	 * some read commands for which cards have no other way of indicating
+	 * they're ready for the next command (i.e. there is no equivalent of
+	 * a "busy" indicator for read operations).
+	 */
+	unsigned int postsleep_min_us;
+	unsigned int postsleep_max_us;
+
+	/*
+	 * Override driver-computed timeouts.  Note the difference in units!
+	 */
+	unsigned int data_timeout_ns;
+	unsigned int cmd_timeout_ms;
+
+	/*
+	 * For 64-bit machines, the next member, ``u64 data_ptr``, wants to
+	 * be 8-byte aligned.  Make sure this struct is the same size when
+	 * built for 32-bit.
+	 */
+	u32 __pad;
+
+	/* DAT buffer */
+	u64 data_ptr;
+};
+#define mmc_ioc_cmd_set_data(ic, ptr) ic.data_ptr = (u64)(unsigned long) ptr
+
+/**
+ * struct mmc_ioc_multi_cmd - multi command information
+ * @num_of_cmds: Number of commands to send. Must be equal to or less than
+ *	MMC_IOC_MAX_CMDS.
+ * @cmds: Array of commands with length equal to 'num_of_cmds'
+ */
+struct mmc_ioc_multi_cmd {
+	u64 num_of_cmds;
+	struct mmc_ioc_cmd cmds[0];
+};
+
+#define MMC_BLOCK_MAJOR 179
+#define MMC_IOC_CMD _IOWR(MMC_BLOCK_MAJOR, 0, struct mmc_ioc_cmd)
+/*
+ * MMC_IOC_MULTI_CMD: Used to send an array of MMC commands described by
+ *	the structure mmc_ioc_multi_cmd. The MMC driver will issue all
+ *	commands in array in sequence to card.
+ */
+#define MMC_IOC_MULTI_CMD _IOWR(MMC_BLOCK_MAJOR, 1, struct mmc_ioc_multi_cmd)
+/*
+ * Since this ioctl is only meant to enhance (and not replace) normal access
+ * to the mmc bus device, an upper data transfer limit of MMC_IOC_MAX_BYTES
+ * is enforced per ioctl call.  For larger data transfers, use the normal
+ * block device operations.
+ */
+#define MMC_IOC_MAX_BYTES  (512L * 1024)
+#define MMC_IOC_MAX_CMDS    255
+#endif /* LINUX_MMC_IOCTL_H */
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/mmc_rpmb.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mmc_rpmb.h
new file mode 100755
index 0000000..237dff7
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mmc_rpmb.h
@@ -0,0 +1,59 @@
+
+
+#ifndef _MMC_RPMB_H
+#define _MMC_RPMB_H
+
+#include "mmc_core.h"
+
+/* ==================================================================================
+
+   RPMB definition
+
+====================================================================================*/
+#define RPMB_SZ_STUFF 196
+#define RPMB_SZ_MAC   32
+#define RPMB_SZ_DATA  256
+#define RPMB_SZ_NONCE 16
+
+#define RPMB_PROGRAM_KEY       1       /* Program RPMB Authentication Key */
+#define RPMB_GET_WRITE_COUNTER 2       /* Read RPMB write counter */
+#define RPMB_WRITE_DATA        3       /* Write data to RPMB partition */
+#define RPMB_READ_DATA         4       /* Read data from RPMB partition */
+#define RPMB_RESULT_READ       5       /* Read result request */
+#define RPMB_REQ               1       /* RPMB request mark */
+#define RPMB_RESP              (1 << 1)/* RPMB response mark */
+#define RPMB_AVALIABLE_SECTORS 8       /* 4K page size */
+
+#define RPMB_TYPE_BEG          510
+#define RPMB_RES_BEG           508
+#define RPMB_BLKS_BEG          506
+#define RPMB_ADDR_BEG          504
+#define RPMB_WCOUNTER_BEG      500
+
+#define RPMB_NONCE_BEG         484
+#define RPMB_DATA_BEG          228
+#define RPMB_MAC_BEG           196
+
+struct mmc_rpmb_cfg {
+    u16 type;                     /* RPMB request type */
+    u16 result;                  /* response or request result */
+    u16 blk_cnt;                  /* Number of blocks(half sector 256B) */
+    u16 addr;                     /* data address */
+    u32 *wc;                      /* write counter */
+    u8 *nonce;                    /* Ramdom number */
+    u8 *data;                     /* Buffer of the user data */
+    u8 *mac;                      /* Message Authentication Code */
+};
+
+struct mmc_rpmb_req {
+    struct mmc_rpmb_cfg *rpmb_cfg;
+    u8 *data_frame;
+};
+
+extern int mmc_rpmb_set_key(u8 *key);
+extern u32 mmc_rpmb_get_size(void);
+extern u32 mmc_rpmb_get_rel_wr_sec_c(void);
+
+extern int mmc_rpmb_block_read(int blknr, unsigned char blk[256]);
+extern int mmc_rpmb_block_write(int blknr, unsigned char blk[256]);
+#endif
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/msdc.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/msdc.h
new file mode 100644
index 0000000..3d0dba4
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/msdc.h
@@ -0,0 +1,1035 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#pragma once
+
+#include <reg.h>
+#include <platform/msdc_cfg.h>
+#include <platform/mt_reg_base.h>
+#include <platform/mmc_core.h>
+
+/*--------------------------------------------------------------------------*/
+/* Common Macro                                                             */
+/*--------------------------------------------------------------------------*/
+#define REG_ADDR(x)             ((volatile uint32_t *)(uintptr_t)(base + OFFSET_##x))
+
+/*--------------------------------------------------------------------------*/
+/* Common Definition                                                        */
+/*--------------------------------------------------------------------------*/
+#define MSDC_FIFO_SZ            (128)
+#define MSDC_FIFO_THD           (128)
+//#define MSDC_MAX_NUM            (2)
+
+#define MSDC_MS                 (0)
+#define MSDC_SDMMC              (1)
+
+#define MSDC_MODE_UNKNOWN       (0)
+#define MSDC_MODE_PIO           (1)
+#define MSDC_MODE_DMA_BASIC     (2)
+#define MSDC_MODE_DMA_DESC      (3)
+#define MSDC_MODE_DMA_ENHANCED  (4)
+#define MSDC_MODE_MMC_STREAM    (5)
+
+#define MSDC_BUS_1BITS          (0)
+#define MSDC_BUS_4BITS          (1)
+#define MSDC_BUS_8BITS          (2)
+
+#define MSDC_BURST_8B           (3)
+#define MSDC_BURST_16B          (4)
+#define MSDC_BURST_32B          (5)
+#define MSDC_BURST_64B          (6)
+
+#define MSDC_PIN_PULL_NONE      (0)
+#define MSDC_PIN_PULL_DOWN      (1)
+#define MSDC_PIN_PULL_UP        (2)
+#define MSDC_PIN_KEEP           (3)
+
+#ifdef FPGA_PLATFORM
+#define MSDC_OP_SCLK            (12000000)
+#define MSDC_MAX_SCLK           MSDC_OP_SCLK / 2
+#else
+#define MSDC_OP_SCLK            (200000000)
+#define MSDC_MAX_SCLK           (200000000)
+#endif
+
+#define MSDC_MIN_SCLK           (260000)
+
+#define MSDC_350K_SCLK          (350000)
+#define MSDC_400K_SCLK          (400000)
+#define MSDC_25M_SCLK           (25000000)
+#define MSDC_26M_SCLK           (26000000)
+#define MSDC_50M_SCLK           (50000000)
+#define MSDC_52M_SCLK           (52000000)
+#define MSDC_100M_SCLK          (100000000)
+#define MSDC_179M_SCLK          (179000000)
+#define MSDC_200M_SCLK          (200000000)
+#define MSDC_208M_SCLK          (208000000)
+#define MSDC_400M_SCLK          (400000000)
+#define MSDC_800M_SCLK          (800000000)
+
+#define MSDC_AUTOCMD12          (0x0001)
+#define MSDC_AUTOCMD23          (0x0002)
+#define MSDC_AUTOCMD19          (0x0003)
+
+#define TYPE_CMD_RESP_EDGE      (0)
+#define TYPE_WRITE_CRC_EDGE     (1)
+#define TYPE_READ_DATA_EDGE     (2)
+#define TYPE_WRITE_DATA_EDGE    (3)
+
+#define START_AT_RISING                 (0x0)
+#define START_AT_FALLING                (0x1)
+#define START_AT_RISING_AND_FALLING     (0x2)
+#define START_AT_RISING_OR_FALLING      (0x3)
+
+#define MSDC_DMA_BURST_8B       (3)
+#define MSDC_DMA_BURST_16B      (4)
+#define MSDC_DMA_BURST_32B      (5)
+#define MSDC_DMA_BURST_64B      (6)
+
+/*--------------------------------------------------------------------------*/
+/* Register Offset                                                          */
+/*--------------------------------------------------------------------------*/
+#define OFFSET_MSDC_CFG                  (0x00)
+#define OFFSET_MSDC_IOCON                (0x04)
+#define OFFSET_MSDC_PS                   (0x08)
+#define OFFSET_MSDC_INT                  (0x0c)
+#define OFFSET_MSDC_INTEN                (0x10)
+#define OFFSET_MSDC_FIFOCS               (0x14)
+#define OFFSET_MSDC_TXDATA               (0x18)
+#define OFFSET_MSDC_RXDATA               (0x1c)
+#define OFFSET_SDC_CFG                   (0x30)
+#define OFFSET_SDC_CMD                   (0x34)
+#define OFFSET_SDC_ARG                   (0x38)
+#define OFFSET_SDC_STS                   (0x3c)
+#define OFFSET_SDC_RESP0                 (0x40)
+#define OFFSET_SDC_RESP1                 (0x44)
+#define OFFSET_SDC_RESP2                 (0x48)
+#define OFFSET_SDC_RESP3                 (0x4c)
+#define OFFSET_SDC_BLK_NUM               (0x50)
+#define OFFSET_SDC_VOL_CHG               (0x54)
+#define OFFSET_SDC_CSTS                  (0x58)
+#define OFFSET_SDC_CSTS_EN               (0x5c)
+#define OFFSET_SDC_DCRC_STS              (0x60)
+#define OFFSET_SDC_AVG_CFG0              (0x64)
+
+/* Only for EMMC Controller 4 registers below */
+#define OFFSET_EMMC_CFG0                 (0x70)
+#define OFFSET_EMMC_CFG1                 (0x74)
+#define OFFSET_EMMC_STS                  (0x78)
+#define OFFSET_EMMC_IOCON                (0x7c)
+
+#define OFFSET_SDC_ACMD_RESP             (0x80)
+#define OFFSET_SDC_ACMD19_TRG            (0x84)
+#define OFFSET_SDC_ACMD19_STS            (0x88)
+#define OFFSET_MSDC_DMA_HIGH4BIT         (0x8C)
+#define OFFSET_MSDC_DMA_SA               (0x90)
+#define OFFSET_MSDC_DMA_CA               (0x94)
+#define OFFSET_MSDC_DMA_CTRL             (0x98)
+#define OFFSET_MSDC_DMA_CFG              (0x9c)
+#define OFFSET_MSDC_DBG_SEL              (0xa0)
+#define OFFSET_MSDC_DBG_OUT              (0xa4)
+#define OFFSET_MSDC_DMA_LEN              (0xa8)
+#define OFFSET_MSDC_PATCH_BIT0           (0xb0)
+#define OFFSET_MSDC_PATCH_BIT1           (0xb4)
+#define OFFSET_MSDC_PATCH_BIT2           (0xb8)
+
+/* Only for SD/SDIO Controller 6 registers below */
+#define OFFSET_DAT0_TUNE_CRC             (0xc0)
+#define OFFSET_DAT1_TUNE_CRC             (0xc4)
+#define OFFSET_DAT2_TUNE_CRC             (0xc8)
+#define OFFSET_DAT3_TUNE_CRC             (0xcc)
+#define OFFSET_CMD_TUNE_CRC              (0xd0)
+#define OFFSET_SDIO_TUNE_WIND            (0xd4)
+
+#define OFFSET_MSDC_PAD_TUNE             (0xF0)
+#define OFFSET_MSDC_PAD_TUNE0            (0xF0)
+#define OFFSET_MSDC_PAD_TUNE1            (0xF4)
+#define OFFSET_MSDC_DAT_RDDLY0           (0xF8)
+#define OFFSET_MSDC_DAT_RDDLY1           (0xFC)
+
+#define OFFSET_MSDC_DAT_RDDLY2           (0x100)
+#define OFFSET_MSDC_DAT_RDDLY3           (0x104)
+
+#define OFFSET_MSDC_HW_DBG               (0x110)
+#define OFFSET_MSDC_VERSION              (0x114)
+#define OFFSET_MSDC_ECO_VER              (0x118)
+
+/* Only for EMMC 5.0 Controller 4 registers below */
+#define OFFSET_EMMC50_PAD_CTL0           (0x180)
+#define OFFSET_EMMC50_PAD_DS_CTL0        (0x184)
+#define OFFSET_EMMC50_PAD_DS_TUNE        (0x188)
+#define OFFSET_EMMC50_PAD_CMD_TUNE       (0x18c)
+#define OFFSET_EMMC50_PAD_DAT01_TUNE     (0x190)
+#define OFFSET_EMMC50_PAD_DAT23_TUNE     (0x194)
+#define OFFSET_EMMC50_PAD_DAT45_TUNE     (0x198)
+#define OFFSET_EMMC50_PAD_DAT67_TUNE     (0x19c)
+#define OFFSET_EMMC51_CFG0               (0x204)
+#define OFFSET_EMMC50_CFG0               (0x208)
+#define OFFSET_EMMC50_CFG1               (0x20c)
+#define OFFSET_EMMC50_CFG2               (0x21c)
+#define OFFSET_EMMC50_CFG3               (0x220)
+#define OFFSET_EMMC50_CFG4               (0x224)
+#define OFFSET_SDC_FIFO_CFG              (0x228)
+/*--------------------------------------------------------------------------*/
+/* Register Address                                                         */
+/*--------------------------------------------------------------------------*/
+/* common register */
+#define MSDC_CFG                         REG_ADDR(MSDC_CFG)
+#define MSDC_IOCON                       REG_ADDR(MSDC_IOCON)
+#define MSDC_PS                          REG_ADDR(MSDC_PS)
+#define MSDC_INT                         REG_ADDR(MSDC_INT)
+#define MSDC_INTEN                       REG_ADDR(MSDC_INTEN)
+#define MSDC_FIFOCS                      REG_ADDR(MSDC_FIFOCS)
+#define MSDC_TXDATA                      REG_ADDR(MSDC_TXDATA)
+#define MSDC_RXDATA                      REG_ADDR(MSDC_RXDATA)
+
+/* sdmmc register */
+#define SDC_CFG                          REG_ADDR(SDC_CFG)
+#define SDC_CMD                          REG_ADDR(SDC_CMD)
+#define SDC_ARG                          REG_ADDR(SDC_ARG)
+#define SDC_STS                          REG_ADDR(SDC_STS)
+#define SDC_RESP0                        REG_ADDR(SDC_RESP0)
+#define SDC_RESP1                        REG_ADDR(SDC_RESP1)
+#define SDC_RESP2                        REG_ADDR(SDC_RESP2)
+#define SDC_RESP3                        REG_ADDR(SDC_RESP3)
+#define SDC_BLK_NUM                      REG_ADDR(SDC_BLK_NUM)
+#define SDC_VOL_CHG                      REG_ADDR(SDC_VOL_CHG)
+#define SDC_CSTS                         REG_ADDR(SDC_CSTS)
+#define SDC_CSTS_EN                      REG_ADDR(SDC_CSTS_EN)
+#define SDC_DCRC_STS                     REG_ADDR(SDC_DCRC_STS)
+#define SDC_AVG_CFG0                     REG_ADDR(SDC_AVG_CFG0)
+
+/* emmc register*/
+#define EMMC_CFG0                        REG_ADDR(EMMC_CFG0)
+#define EMMC_CFG1                        REG_ADDR(EMMC_CFG1)
+#define EMMC_STS                         REG_ADDR(EMMC_STS)
+#define EMMC_IOCON                       REG_ADDR(EMMC_IOCON)
+
+/* auto command register */
+#define SDC_ACMD_RESP                    REG_ADDR(SDC_ACMD_RESP)
+#define SDC_ACMD19_TRG                   REG_ADDR(SDC_ACMD19_TRG)
+#define SDC_ACMD19_STS                   REG_ADDR(SDC_ACMD19_STS)
+
+/* dma register */
+#define MSDC_DMA_HIGH4BIT                REG_ADDR(MSDC_DMA_HIGH4BIT)
+#define MSDC_DMA_SA                      REG_ADDR(MSDC_DMA_SA)
+#define MSDC_DMA_CA                      REG_ADDR(MSDC_DMA_CA)
+#define MSDC_DMA_CTRL                    REG_ADDR(MSDC_DMA_CTRL)
+#define MSDC_DMA_CFG                     REG_ADDR(MSDC_DMA_CFG)
+
+/* debug register */
+#define MSDC_DBG_SEL                     REG_ADDR(MSDC_DBG_SEL)
+#define MSDC_DBG_OUT                     REG_ADDR(MSDC_DBG_OUT)
+#define MSDC_DMA_LEN                     REG_ADDR(MSDC_DMA_LEN)
+
+/* misc register */
+#define MSDC_PATCH_BIT0                  REG_ADDR(MSDC_PATCH_BIT0)
+#define MSDC_PATCH_BIT1                  REG_ADDR(MSDC_PATCH_BIT1)
+#define MSDC_PATCH_BIT2                  REG_ADDR(MSDC_PATCH_BIT2)
+#define DAT0_TUNE_CRC                    REG_ADDR(DAT0_TUNE_CRC)
+#define DAT1_TUNE_CRC                    REG_ADDR(DAT1_TUNE_CRC)
+#define DAT2_TUNE_CRC                    REG_ADDR(DAT2_TUNE_CRC)
+#define DAT3_TUNE_CRC                    REG_ADDR(DAT3_TUNE_CRC)
+#define CMD_TUNE_CRC                     REG_ADDR(CMD_TUNE_CRC)
+#define SDIO_TUNE_WIND                   REG_ADDR(SDIO_TUNE_WIND)
+#define MSDC_PAD_TUNE                    REG_ADDR(MSDC_PAD_TUNE)
+#define MSDC_PAD_TUNE0                   REG_ADDR(MSDC_PAD_TUNE0)
+#define MSDC_PAD_TUNE1                   REG_ADDR(MSDC_PAD_TUNE1)
+
+/* data read delay */
+#define MSDC_DAT_RDDLY0                  REG_ADDR(MSDC_DAT_RDDLY0)
+#define MSDC_DAT_RDDLY1                  REG_ADDR(MSDC_DAT_RDDLY1)
+#define MSDC_DAT_RDDLY2                  REG_ADDR(MSDC_DAT_RDDLY2)
+#define MSDC_DAT_RDDLY3                  REG_ADDR(MSDC_DAT_RDDLY3)
+
+#define MSDC_HW_DBG                      REG_ADDR(MSDC_HW_DBG)
+#define MSDC_VERSION                     REG_ADDR(MSDC_VERSION)
+#define MSDC_ECO_VER                     REG_ADDR(MSDC_ECO_VER)
+/* eMMC 5.0 register */
+#define EMMC50_PAD_CTL0                  REG_ADDR(EMMC50_PAD_CTL0)
+#define EMMC50_PAD_DS_CTL0               REG_ADDR(EMMC50_PAD_DS_CTL0)
+#define EMMC50_PAD_DS_TUNE               REG_ADDR(EMMC50_PAD_DS_TUNE)
+#define EMMC50_PAD_CMD_TUNE              REG_ADDR(EMMC50_PAD_CMD_TUNE)
+#define EMMC50_PAD_DAT01_TUNE            REG_ADDR(EMMC50_PAD_DAT01_TUNE)
+#define EMMC50_PAD_DAT23_TUNE            REG_ADDR(EMMC50_PAD_DAT23_TUNE)
+#define EMMC50_PAD_DAT45_TUNE            REG_ADDR(EMMC50_PAD_DAT45_TUNE)
+#define EMMC50_PAD_DAT67_TUNE            REG_ADDR(EMMC50_PAD_DAT67_TUNE)
+#define EMMC51_CFG0                      REG_ADDR(EMMC51_CFG0)
+#define EMMC50_CFG0                      REG_ADDR(EMMC50_CFG0)
+#define EMMC50_CFG1                      REG_ADDR(EMMC50_CFG1)
+#define EMMC50_CFG2                      REG_ADDR(EMMC50_CFG2)
+#define EMMC50_CFG3                      REG_ADDR(EMMC50_CFG3)
+#define EMMC50_CFG4                      REG_ADDR(EMMC50_CFG4)
+#define SDC_FIFO_CFG                     REG_ADDR(SDC_FIFO_CFG)
+
+/*--------------------------------------------------------------------------*/
+/* Register Mask                                                            */
+/*--------------------------------------------------------------------------*/
+
+/* MSDC_CFG mask */
+#define MSDC_CFG_MODE           (0x1   <<  0)     /* RW */
+#define MSDC_CFG_CKPDN          (0x1   <<  1)     /* RW */
+#define MSDC_CFG_RST            (0x1   <<  2)     /* A0 */
+#define MSDC_CFG_PIO            (0x1   <<  3)     /* RW */
+#define MSDC_CFG_CKDRVEN        (0x1   <<  4)     /* RW */
+#define MSDC_CFG_BV18SDT        (0x1   <<  5)     /* RW */
+#define MSDC_CFG_BV18PSS        (0x1   <<  6)     /* R  */
+#define MSDC_CFG_CKSTB          (0x1   <<  7)     /* R  */
+#define MSDC_CFG_CKDIV          (0xFFF <<  8)     /* RW   !!! MT2701 change 0xFF ->0xFFF*/
+#define MSDC_CFG_CKMOD          (0x3   << 20)     /* W1C  !!! MT2701 change 16 ->21 only for eMCC 5.0*/
+#define MSDC_CFG_CKMOD_HS400    (0x1   << 22)     /* RW   !!! MT2701 change 18 ->22 only for eMCC 5.0*/
+#define MSDC_CFG_START_BIT      (0x3   << 23)     /* RW   !!! MT2701 change 19 ->23 only for eMCC 5.0*/
+#define MSDC_CFG_SCLK_STOP_DDR  (0x1   << 25)     /* RW   !!! MT2701 change 21 ->25 */
+
+/* MSDC_IOCON mask */
+#define MSDC_IOCON_SDR104CKS    (0x1   <<  0)     /* RW */
+#define MSDC_IOCON_RSPL         (0x1   <<  1)     /* RW */
+#define MSDC_IOCON_R_D_SMPL     (0x1   <<  2)     /* RW */
+#define MSDC_IOCON_DSPL          MSDC_IOCON_R_D_SMPL /* alias */
+
+#define MSDC_IOCON_DDLSEL       (0x1   <<  3)     /* RW */
+#define MSDC_IOCON_DDR50CKD     (0x1   <<  4)     /* RW */
+#define MSDC_IOCON_R_D_SMPL_SEL (0x1   <<  5)     /* RW */
+#define MSDC_IOCON_W_D_SMPL     (0x1   <<  8)     /* RW */
+#define MSDC_IOCON_W_D_SMPL_SEL (0x1   <<  9)     /* RW */
+#define MSDC_IOCON_W_D0SPL      (0x1   << 10)     /* RW */
+#define MSDC_IOCON_W_D1SPL      (0x1   << 11)     /* RW */
+#define MSDC_IOCON_W_D2SPL      (0x1   << 12)     /* RW */
+#define MSDC_IOCON_W_D3SPL      (0x1   << 13)     /* RW */
+
+#define MSDC_IOCON_R_D0SPL      (0x1   << 16)     /* RW */
+#define MSDC_IOCON_R_D1SPL      (0x1   << 17)     /* RW */
+#define MSDC_IOCON_R_D2SPL      (0x1   << 18)     /* RW */
+#define MSDC_IOCON_R_D3SPL      (0x1   << 19)     /* RW */
+#define MSDC_IOCON_R_D4SPL      (0x1   << 20)     /* RW */
+#define MSDC_IOCON_R_D5SPL      (0x1   << 21)     /* RW */
+#define MSDC_IOCON_R_D6SPL      (0x1   << 22)     /* RW */
+#define MSDC_IOCON_R_D7SPL      (0x1   << 23)     /* RW */
+//#define MSDC_IOCON_RISCSZ       (0x3   << 24)     /* RW  !!! MT2701  remove*/
+
+/* MSDC_PS mask */
+#define MSDC_PS_CDEN            (0x1   <<  0)     /* RW */
+#define MSDC_PS_CDSTS           (0x1   <<  1)     /* RU  */
+
+#define MSDC_PS_CDDEBOUNCE      (0xF   << 12)     /* RW */
+#define MSDC_PS_DAT             (0xFF  << 16)     /* RU */
+#define MSDC_PS_DAT8PIN         (0xFF  << 16)     /* RU */
+#define MSDC_PS_DAT4PIN         (0xF   << 16)     /* RU */
+#define MSDC_PS_DAT0            (0x1   << 16)     /* RU */
+
+#define MSDC_PS_CMD             (0x1   << 24)     /* RU */
+
+#define MSDC_PS_WP              (0x1   << 31)     /* RU  */
+
+/* MSDC_INT mask */
+#define MSDC_INT_MMCIRQ         (0x1   <<  0)     /* W1C */
+#define MSDC_INT_CDSC           (0x1   <<  1)     /* W1C */
+
+#define MSDC_INT_ACMDRDY        (0x1   <<  3)     /* W1C */
+#define MSDC_INT_ACMDTMO        (0x1   <<  4)     /* W1C */
+#define MSDC_INT_ACMDCRCERR     (0x1   <<  5)     /* W1C */
+#define MSDC_INT_DMAQ_EMPTY     (0x1   <<  6)     /* W1C */
+#define MSDC_INT_SDIOIRQ        (0x1   <<  7)     /* W1C Only for SD/SDIO */
+#define MSDC_INT_CMDRDY         (0x1   <<  8)     /* W1C */
+#define MSDC_INT_CMDTMO         (0x1   <<  9)     /* W1C */
+#define MSDC_INT_RSPCRCERR      (0x1   << 10)     /* W1C */
+#define MSDC_INT_CSTA           (0x1   << 11)     /* R */
+#define MSDC_INT_XFER_COMPL     (0x1   << 12)     /* W1C */
+#define MSDC_INT_DXFER_DONE     (0x1   << 13)     /* W1C */
+#define MSDC_INT_DATTMO         (0x1   << 14)     /* W1C */
+#define MSDC_INT_DATCRCERR      (0x1   << 15)     /* W1C */
+#define MSDC_INT_ACMD19_DONE    (0x1   << 16)     /* W1C */
+#define MSDC_INT_BDCSERR        (0x1   << 17)     /* W1C */
+#define MSDC_INT_GPDCSERR       (0x1   << 18)     /* W1C */
+#define MSDC_INT_DMAPRO         (0x1   << 19)     /* W1C */
+#define MSDC_INT_GOBOUND        (0x1   << 20)     /* W1C Only for SD/SDIO ACMD 53*/
+#define MSDC_INT_ACMD53_DONE    (0x1   << 21)     /* W1C Only for SD/SDIO ACMD 53*/
+#define MSDC_INT_ACMD53_FAIL    (0x1   << 22)     /* W1C Only for SD/SDIO ACMD 53*/
+#define MSDC_INT_AXI_RESP_ERR   (0x1   << 23)     /* W1C Only for eMMC 5.0*/
+
+/* MSDC_INTEN mask */
+#define MSDC_INTEN_MMCIRQ       (0x1   <<  0)     /* RW */
+#define MSDC_INTEN_CDSC         (0x1   <<  1)     /* RW */
+
+#define MSDC_INTEN_ACMDRDY      (0x1   <<  3)     /* RW */
+#define MSDC_INTEN_ACMDTMO      (0x1   <<  4)     /* RW */
+#define MSDC_INTEN_ACMDCRCERR   (0x1   <<  5)     /* RW */
+#define MSDC_INTEN_DMAQ_EMPTY   (0x1   <<  6)     /* RW */
+#define MSDC_INTEN_SDIOIRQ      (0x1   <<  7)     /* RW Only for SDIO*/
+#define MSDC_INTEN_CMDRDY       (0x1   <<  8)     /* RW */
+#define MSDC_INTEN_CMDTMO       (0x1   <<  9)     /* RW */
+#define MSDC_INTEN_RSPCRCERR    (0x1   << 10)     /* RW */
+#define MSDC_INTEN_CSTA         (0x1   << 11)     /* RW */
+#define MSDC_INTEN_XFER_COMPL   (0x1   << 12)     /* RW */
+#define MSDC_INTEN_DXFER_DONE   (0x1   << 13)     /* RW */
+#define MSDC_INTEN_DATTMO       (0x1   << 14)     /* RW */
+#define MSDC_INTEN_DATCRCERR    (0x1   << 15)     /* RW */
+#define MSDC_INTEN_ACMD19_DONE  (0x1   << 16)     /* RW */
+#define MSDC_INTEN_BDCSERR      (0x1   << 17)     /* RW */
+#define MSDC_INTEN_GPDCSERR     (0x1   << 18)     /* RW */
+#define MSDC_INTEN_DMAPRO       (0x1   << 19)     /* RW */
+#define MSDC_INTEN_GOBOUND      (0x1   << 20)     /* RW  Only for SD/SDIO ACMD 53*/
+#define MSDC_INTEN_ACMD53_DONE  (0x1   << 21)     /* RW  Only for SD/SDIO ACMD 53*/
+#define MSDC_INTEN_ACMD53_FAIL  (0x1   << 22)     /* RW  Only for SD/SDIO ACMD 53*/
+#define MSDC_INTEN_AXI_RESP_ERR (0x1   << 23)     /* RW  Only for eMMC 5.0*/
+
+#define MSDC_INTEN_DFT       (  MSDC_INTEN_MMCIRQ        |MSDC_INTEN_CDSC        | MSDC_INTEN_ACMDRDY\
+                                |MSDC_INTEN_ACMDTMO      |MSDC_INTEN_ACMDCRCERR  | MSDC_INTEN_DMAQ_EMPTY /*|MSDC_INTEN_SDIOIRQ*/\
+                                |MSDC_INTEN_CMDRDY       |MSDC_INTEN_CMDTMO      | MSDC_INTEN_RSPCRCERR   |MSDC_INTEN_CSTA\
+                                |MSDC_INTEN_XFER_COMPL   |MSDC_INTEN_DXFER_DONE  | MSDC_INTEN_DATTMO      |MSDC_INTEN_DATCRCERR\
+                                |MSDC_INTEN_BDCSERR      |MSDC_INTEN_ACMD19_DONE | MSDC_INTEN_GPDCSERR     /*|MSDC_INTEN_DMAPRO*/\
+                                /*|MSDC_INTEN_GOBOUND   |MSDC_INTEN_ACMD53_DONE |MSDC_INTEN_ACMD53_FAIL |MSDC_INTEN_AXI_RESP_ERR*/)
+
+
+/* MSDC_FIFOCS mask */
+#define MSDC_FIFOCS_RXCNT       (0xFF  <<  0)     /* R */
+#define MSDC_FIFOCS_TXCNT       (0xFF  << 16)     /* R */
+#define MSDC_FIFOCS_CLR         (0x1   << 31)     /* RW */
+
+/* SDC_CFG mask */
+#define SDC_CFG_SDIOINTWKUP     (0x1   <<  0)     /* RW */
+#define SDC_CFG_INSWKUP         (0x1   <<  1)     /* RW */
+#define SDC_CFG_BUSWIDTH        (0x3   << 16)     /* RW */
+#define SDC_CFG_SDIO            (0x1   << 19)     /* RW */
+#define SDC_CFG_SDIOIDE         (0x1   << 20)     /* RW */
+#define SDC_CFG_INTATGAP        (0x1   << 21)     /* RW */
+#define SDC_CFG_DTOC            (0xFF  << 24)     /* RW */
+
+/* SDC_CMD mask */
+#define SDC_CMD_OPC             (0x3F  <<  0)     /* RW */
+#define SDC_CMD_BRK             (0x1   <<  6)     /* RW */
+#define SDC_CMD_RSPTYP          (0x7   <<  7)     /* RW */
+#define SDC_CMD_DTYP            (0x3   << 11)     /* RW */
+#define SDC_CMD_RW              (0x1   << 13)     /* RW */
+#define SDC_CMD_STOP            (0x1   << 14)     /* RW */
+#define SDC_CMD_GOIRQ           (0x1   << 15)     /* RW */
+#define SDC_CMD_BLKLEN          (0xFFF << 16)     /* RW */
+#define SDC_CMD_AUTOCMD         (0x3   << 28)     /* RW */
+#define SDC_CMD_VOLSWTH         (0x1   << 30)     /* RW */
+#define SDC_CMD_ACMD53          (0x1   << 31)     /* RW Only for SD/SDIO ACMD 53*/
+
+/* SDC_STS mask */
+#define SDC_STS_SDCBUSY         (0x1   <<  0)     /* RW */
+#define SDC_STS_CMDBUSY         (0x1   <<  1)     /* RW */
+#define SDC_STS_CMD_WR_BUSY     (0x1   << 16)     /* RW !!! MT2701  Add*/
+#define SDC_STS_SWR_COMPL       (0x1   << 31)     /* RW */
+
+/* SDC_VOL_CHG mask */
+#define SDC_VOL_CHG_VCHGCNT     (0xFFFF<<  0)     /* RW  !!! MT2701  Add*/
+/* SDC_DCRC_STS mask */
+#define SDC_DCRC_STS_POS        (0xFF  <<  0)     /* RO */
+#define SDC_DCRC_STS_NEG        (0xFF  <<  8)     /* RO */
+/* SDC_ADV_CFG0 mask */
+#define SDC_RX_ENHANCE_EN      (0x1 << 20)     /* RW */
+/* EMMC_CFG0 mask */
+#define EMMC_CFG0_BOOTSTART     (0x1   <<  0)     /* WO Only for eMMC */
+#define EMMC_CFG0_BOOTSTOP      (0x1   <<  1)     /* WO Only for eMMC */
+#define EMMC_CFG0_BOOTMODE      (0x1   <<  2)     /* RW Only for eMMC */
+#define EMMC_CFG0_BOOTACKDIS    (0x1   <<  3)     /* RW Only for eMMC */
+
+#define EMMC_CFG0_BOOTWDLY      (0x7   << 12)     /* RW Only for eMMC */
+#define EMMC_CFG0_BOOTSUPP      (0x1   << 15)     /* RW Only for eMMC */
+
+/* EMMC_CFG1 mask */
+#define EMMC_CFG1_BOOTDATTMC   (0xFFFFF<<  0)     /* RW Only for eMMC */
+#define EMMC_CFG1_BOOTACKTMC    (0xFFF << 20)     /* RW Only for eMMC */
+
+/* EMMC_STS mask */
+#define EMMC_STS_BOOTCRCERR     (0x1   <<  0)     /* W1C Only for eMMC */
+#define EMMC_STS_BOOTACKERR     (0x1   <<  1)     /* W1C Only for eMMC */
+#define EMMC_STS_BOOTDATTMO     (0x1   <<  2)     /* W1C Only for eMMC */
+#define EMMC_STS_BOOTACKTMO     (0x1   <<  3)     /* W1C Only for eMMC */
+#define EMMC_STS_BOOTUPSTATE    (0x1   <<  4)     /* RU Only for eMMC */
+#define EMMC_STS_BOOTACKRCV     (0x1   <<  5)     /* W1C Only for eMMC */
+#define EMMC_STS_BOOTDATRCV     (0x1   <<  6)     /* RU Only for eMMC */
+
+/* EMMC_IOCON mask */
+#define EMMC_IOCON_BOOTRST      (0x1   <<  0)     /* RW Only for eMMC */
+
+/* SDC_ACMD19_TRG mask */
+#define SDC_ACMD19_TRG_TUNESEL  (0xF   <<  0)     /* RW */
+
+/* DMA_SA_HIGH4BIT mask */
+#define DMA_SA_HIGH4BIT_L4BITS  (0xF   <<  0)     /* RW  !!! MT2701  Add*/
+/* MSDC_DMA_CTRL mask */
+#define MSDC_DMA_CTRL_START     (0x1   <<  0)     /* WO */
+#define MSDC_DMA_CTRL_STOP      (0x1   <<  1)     /* AO */
+#define MSDC_DMA_CTRL_RESUME    (0x1   <<  2)     /* WO */
+#define MSDC_DMA_CTRL_READYM    (0x1   <<  3)     /* RO  !!! MT2701  Add*/
+
+#define MSDC_DMA_CTRL_MODE      (0x1   <<  8)     /* RW */
+#define MSDC_DMA_CTRL_ALIGN     (0x1   <<  9)     /* RW !!! MT2701  Add*/
+#define MSDC_DMA_CTRL_LASTBUF   (0x1   << 10)     /* RW */
+#define MSDC_DMA_CTRL_SPLIT1K   (0x1   << 11)     /* RW !!! MT2701  Add*/
+#define MSDC_DMA_CTRL_BURSTSZ   (0x7   << 12)     /* RW */
+// #define MSDC_DMA_CTRL_XFERSZ    (0xffffUL << 16)/* RW */
+
+/* MSDC_DMA_CFG mask */
+#define MSDC_DMA_CFG_STS              (0x1  <<  0)     /* R */
+#define MSDC_DMA_CFG_DECSEN           (0x1  <<  1)     /* RW */
+#define MSDC_DMA_CFG_LOCKDISABLE      (0x1  <<  2)     /* RW !!! MT2701  Add*/
+//#define MSDC_DMA_CFG_BDCSERR        (0x1  <<  4)     /* R */
+//#define MSDC_DMA_CFG_GPDCSERR       (0x1  <<  5)     /* R */
+#define MSDC_DMA_CFG_AHBEN            (0x3  <<  8)     /* RW */
+#define MSDC_DMA_CFG_ACTEN            (0x3  << 12)     /* RW */
+
+#define MSDC_DMA_CFG_CS12B            (0x1  << 16)     /* RW */
+#define MSDC_DMA_CFG_OUTB_STOP        (0x1  << 17)     /* RW */
+
+/* MSDC_PATCH_BIT0 mask */
+//#define MSDC_PB0_RESV1              (0x1  <<  0)
+#define MSDC_PB0_EN_8BITSUP           (0x1  <<  1)    /* RW */
+#define MSDC_PB0_DIS_RECMDWR          (0x1  <<  2)    /* RW */
+//#define MSDC_PB0_RESV2                        (0x1  <<  3)
+#define MSDC_PB0_RDDATSEL             (0x1  <<  3)    /* RW !!! MT2701 Add for SD/SDIO/eMMC 4.5*/
+#define MSDC_PB0_ACMD53_CRCINTR       (0x1  <<  4)    /* RW !!! MT2701 Add only for SD/SDIO */
+#define MSDC_PB0_ACMD53_ONESHOT       (0x1  <<  5)    /* RW !!! MT2701 Add ony for SD/SDIO */
+//#define MSDC_PB0_RESV3                        (0x1  <<  6)
+#define MSDC_PB0_DESC_UP_SEL          (0x1  <<  6)    /* RW !!! MT2701  Add*/
+#define MSDC_PB0_INT_DAT_LATCH_CK_SEL (0x7  <<  7)    /* RW */
+#define MSDC_INT_DAT_LATCH_CK_SEL      MSDC_PB0_INT_DAT_LATCH_CK_SEL  /* alias */
+
+#define MSDC_PB0_CKGEN_MSDC_DLY_SEL   (0x1F << 10)    /* RW */
+#define MSDC_CKGEN_MSDC_DLY_SEL        MSDC_PB0_CKGEN_MSDC_DLY_SEL  /* alias */
+
+
+#define MSDC_PB0_FIFORD_DIS           (0x1  << 15)    /* RW */
+//#define MSDC_PB0_SDIO_DBSSEL                  (0x1  << 16)    /* RW !!! MT2701  change*/
+#define MSDC_PB0_MSDC_BLKNUMSEL       (0x1  << 16)    /* RW !!! MT2701  change ACMD23*/
+#define MSDC_PB0_BLKNUM_SEL           MSDC_PB0_MSDC_BLKNUMSEL /* alias */
+
+#define MSDC_PB0_SDIO_INTCSEL         (0x1  << 17)    /* RW */
+#define MSDC_PB0_SDIO_BSYDLY          (0xF  << 18)    /* RW */
+#define MSDC_PB0_SDC_WDOD             (0xF  << 22)    /* RW */
+#define MSDC_PB0_CMDIDRTSEL           (0x1  << 26)    /* RW */
+#define MSDC_PB0_CMDFAILSEL           (0x1  << 27)    /* RW */
+#define MSDC_PB0_SDIO_INTDLYSEL       (0x1  << 28)    /* RW */
+#define MSDC_PB0_SPCPUSH              (0x1  << 29)    /* RW */
+#define MSDC_PB0_DETWR_CRCTMO         (0x1  << 30)    /* RW */
+#define MSDC_PB0_EN_DRVRSP            (0x1  << 31)    /* RW */
+
+/* MSDC_PATCH_BIT1 mask */
+#define MSDC_PB1_WRDAT_CRCS_TA_CNTR   (0x7  <<  0)    /* RW */
+#define MSDC_PATCH_BIT1_WRDAT_CRCS    MSDC_PB1_WRDAT_CRCS_TA_CNTR /* alias */
+
+
+#define MSDC_PB1_CMD_RSP_TA_CNTR      (0x7  <<  3)    /* RW */
+#define MSDC_PATCH_BIT1_CMD_RSP       MSDC_PB1_CMD_RSP_TA_CNTR  /* alias */
+
+//#define MSDC_PB1_RESV3                        (0x3  <<  6)
+#define MSDC_PB1_GET_BUSY_MARGIN      (0x1  <<  6)    /* RW !!! MT2701  Add */
+#define MSDC_BUSY_CHECK_SEL           (0x1  <<  7)    /* RW !!! MT2712  Add */
+#define MSDC_PB1_BIAS_TUNE_28NM       (0xF  <<  8)    /* RW */
+#define MSDC_PB1_BIAS_EN18IO_28NM     (0x1  << 12)    /* RW */
+#define MSDC_PB1_BIAS_EXT_28NM        (0x1  << 13)    /* RW */
+
+//#define MSDC_PB1_RESV2                        (0x3  << 14)
+#define MSDC_PB1_RESET_GDMA           (0x1  << 15)    /* RW !!! MT2701  Add */
+//#define MSDC_PB1_RESV1                        (0x7F << 16)
+#define MSDC_PB1_EN_SINGLE_BURST      (0x1  << 16)    /* RW !!! MT2701  Add */
+#define MSDC_PB1_EN_FORCE_STOP_GDMA   (0x1  << 17)    /* RW !!! MT2701  Add  for eMMC 5.0 only*/
+#define MSDC_PB1_DCM_DIV_SEL2         (0x3  << 18)    /* RW !!! MT2701  Add  for eMMC 5.0 only*/
+#define MSDC_PB1_DCM_DIV_SEL1         (0x1  << 20)    /* RW !!! MT2701  Add */
+#define MSDC_PB1_DCM_EN               (0x1  << 21)    /* RW !!! MT2701  Add */
+#define MSDC_PB1_AXI_WRAP_CKEN        (0x1  << 22)    /* RW !!! MT2701  Add for eMMC 5.0 only*/
+#define MSDC_PB1_AHBCKEN              (0x1  << 23)    /* RW */
+#define MSDC_PB1_CKSPCEN              (0x1  << 24)    /* RW */
+#define MSDC_PB1_CKPSCEN              (0x1  << 25)    /* RW */
+#define MSDC_PB1_CKVOLDETEN           (0x1  << 26)    /* RW */
+#define MSDC_PB1_CKACMDEN             (0x1  << 27)    /* RW */
+#define MSDC_PB1_CKSDEN               (0x1  << 28)    /* RW */
+#define MSDC_PB1_CKWCTLEN             (0x1  << 29)    /* RW */
+#define MSDC_PB1_CKRCTLEN             (0x1  << 30)    /* RW */
+#define MSDC_PB1_CKSHBFFEN            (0x1  << 31)    /* RW */
+
+/* MSDC_PATCH_BIT2 mask */
+#define MSDC_PB2_ENHANCEGPD           (0x1  <<  0)    /* RW !!! MT2701  Add */
+#define MSDC_PB2_SUPPORT64G           (0x1  <<  1)    /* RW !!! MT2701  Add */
+#define MSDC_PB2_RESPWAIT             (0x3  <<  2)    /* RW !!! MT2701  Add */
+#define MSDC_PB2_CFGRDATCNT           (0x1F <<  4)    /* RW !!! MT2701  Add */
+#define MSDC_PB2_CFGRDAT              (0x1  <<  9)    /* RW !!! MT2701  Add */
+
+#define MSDC_PB2_INTCRESPSEL          (0x1  << 11)    /* RW !!! MT2701  Add */
+#define MSDC_PB2_CFGRESPCNT           (0x7  << 12)    /* RW !!! MT2701  Add */
+#define MSDC_PB2_CFGRESP              (0x1  << 15)    /* RW !!! MT2701  Add */
+#define MSDC_PB2_RESPSTSENSEL         (0x7  << 16)    /* RW !!! MT2701  Add */
+
+#define MSDC_PB2_POPENCNT             (0xF  << 20)    /* RW !!! MT2701  Add */
+#define MSDC_PB2_CFGCRCSTSSEL         (0x1  << 24)    /* RW !!! MT2701  Add */
+#define MSDC_PB2_CFGCRCSTSEDGE        (0x1  << 25)    /* RW !!! MT2701  Add */
+#define MSDC_PB2_CFGCRCSTSCNT         (0x3  << 26)    /* RW !!! MT2701  Add */
+#define MSDC_PB2_CFGCRCSTS            (0x1  << 28)    /* RW !!! MT2701  Add */
+#define MSDC_PB2_CRCSTSENSEL          (0x7  << 29)    /* RW !!! MT2701  Add */
+
+
+/* SDIO_TUNE_WIND mask */
+#define SDIO_TUNE_WIND_TUNEWINDOW     (0x1F  <<  0)     /* RW !!! MT2701  Add for SD/SDIO only*/
+
+/* MSDC_PAD_TUNE/MSDC_PAD_TUNE0 mask */
+#define MSDC_PAD_TUNE_DATWRDLY        (0x1F  <<  0)     /* RW */
+
+#define MSDC_PAD_TUNE_DELAYEN         (0x1   <<  7)     /* RW !!! MT2701  Add*/
+#define MSDC_PAD_TUNE_DATRRDLY        (0x1F  <<  8)     /* RW */
+#define MSDC_PAD_TUNE_DATRRDLYSEL     (0x1   << 13)     /* RW !!! MT2701  Add*/
+
+#define MSDC_PAD_TUNE_RXDLYSEL        (0x1   << 15)     /* RW !!! MT2701  Add*/
+#define MSDC_PAD_TUNE_CMDRDLY         (0x1F  << 16)     /* RW */
+#define MSDC_PAD_TUNE_CMDRDLYSEL      (0x1   << 21)     /* RW !!! MT2701  Add*/
+#define MSDC_PAD_TUNE_CMDRRDLY        (0x1F  << 22)     /* RW */
+#define MSDC_PAD_TUNE_CLKTXDLY        (0x1F  << 27)     /* RW */
+
+/* MSDC_PAD_TUNE1 mask */
+
+#define MSDC_PAD_TUNE1_DATRRDLY2      (0x1F  <<  8)     /* RW  !!! MT2701  Add*/
+#define MSDC_PAD_TUNE1_DATRDLY2SEL    (0x1   << 13)     /* RW  !!! MT2701  Add*/
+
+#define MSDC_PAD_TUNE1_CMDRDLY2       (0x1F  << 16)     /* RW  !!! MT2701  Add*/
+#define MSDC_PAD_TUNE1_CMDRDLY2SEL    (0x1   << 21)     /* RW  !!! MT2701  Add*/
+
+
+/* MSDC_DAT_RDDLY0 mask */
+#define MSDC_DAT_RDDLY0_D3            (0x1F  <<  0)     /* RW */
+#define MSDC_DAT_RDDLY0_D2            (0x1F  <<  8)     /* RW */
+#define MSDC_DAT_RDDLY0_D1            (0x1F  << 16)     /* RW */
+#define MSDC_DAT_RDDLY0_D0            (0x1F  << 24)     /* RW */
+
+/* MSDC_DAT_RDDLY1 mask */
+#define MSDC_DAT_RDDLY1_D7            (0x1F  <<  0)     /* RW */
+
+#define MSDC_DAT_RDDLY1_D6            (0x1F  <<  8)     /* RW */
+
+#define MSDC_DAT_RDDLY1_D5            (0x1F  << 16)     /* RW */
+
+#define MSDC_DAT_RDDLY1_D4            (0x1F  << 24)     /* RW */
+
+/* MSDC_DAT_RDDLY2 mask */
+#define MSDC_DAT_RDDLY2_D3            (0x1F  <<  0)     /* RW !!! MT2701  Add*/
+
+#define MSDC_DAT_RDDLY2_D2            (0x1F  <<  8)     /* RW !!! MT2701  Add*/
+
+#define MSDC_DAT_RDDLY2_D1            (0x1F  << 16)     /* RW !!! MT2701  Add*/
+
+#define MSDC_DAT_RDDLY2_D0            (0x1F  << 24)     /* RW !!! MT2701  Add*/
+
+/* MSDC_DAT_RDDLY3 mask */
+#define MSDC_DAT_RDDLY3_D7            (0x1F  <<  0)     /* RW !!! MT2701  Add*/
+
+#define MSDC_DAT_RDDLY3_D6            (0x1F  <<  8)     /* RW !!! MT2701  Add*/
+
+#define MSDC_DAT_RDDLY3_D5            (0x1F  << 16)     /* RW !!! MT2701  Add*/
+
+#define MSDC_DAT_RDDLY3_D4            (0x1F  << 24)     /* RW !!! MT2701  Add*/
+
+/* MSDC_HW_DBG_SEL mask */
+#define MSDC_HW_DBG0_SEL              (0xFF  <<  0)     /* RW DBG3->DBG0 !!! MT2701  Change*/
+#define MSDC_HW_DBG1_SEL              (0x3F  <<  8)     /* RW DBG2->DBG1 !!! MT2701  Add*/
+
+#define MSDC_HW_DBG2_SEL              (0xFF  << 16)     /* RW DBG1->DBG2 !!! MT2701  Add*/
+//#define MSDC_HW_DBG_WRAPTYPE_SEL    (0x3   << 22)           /* RW !!! MT2701  Removed*/
+#define MSDC_HW_DBG3_SEL              (0x3F  << 24)     /* RW DBG0->DBG3 !!! MT2701  Add*/
+#define MSDC_HW_DBG_WRAP_SEL          (0x1   << 30)     /* RW */
+
+
+/* MSDC_EMMC50_PAD_CTL0 mask*/
+#define MSDC_EMMC50_PAD_CTL0_DCCSEL   (0x1  <<  0)     /* RW */
+#define MSDC_EMMC50_PAD_CTL0_HLSEL    (0x1  <<  1)     /* RW */
+#define MSDC_EMMC50_PAD_CTL0_DLP0     (0x3  <<  2)     /* RW */
+#define MSDC_EMMC50_PAD_CTL0_DLN0     (0x3  <<  4)     /* RW */
+#define MSDC_EMMC50_PAD_CTL0_DLP1     (0x3  <<  6)     /* RW */
+#define MSDC_EMMC50_PAD_CTL0_DLN1     (0x3  <<  8)     /* RW */
+
+/* MSDC_EMMC50_PAD_DS_CTL0 mask */
+#define MSDC_EMMC50_PAD_DS_CTL0_SR    (0x1  <<  0)     /* RW */
+#define MSDC_EMMC50_PAD_DS_CTL0_R0    (0x1  <<  1)     /* RW */
+#define MSDC_EMMC50_PAD_DS_CTL0_R1    (0x1  <<  2)     /* RW */
+#define MSDC_EMMC50_PAD_DS_CTL0_PUPD  (0x1  <<  3)     /* RW */
+#define MSDC_EMMC50_PAD_DS_CTL0_IES   (0x1  <<  4)     /* RW */
+#define MSDC_EMMC50_PAD_DS_CTL0_SMT   (0x1  <<  5)     /* RW */
+#define MSDC_EMMC50_PAD_DS_CTL0_RDSEL (0x3F <<  6)     /* RW */
+#define MSDC_EMMC50_PAD_DS_CTL0_TDSEL (0xF  << 12)     /* RW */
+#define MSDC_EMMC50_PAD_DS_CTL0_DRV   (0x7  << 16)     /* RW */
+
+
+/* EMMC50_PAD_DS_TUNE mask */
+#define MSDC_EMMC50_PAD_DS_TUNE_DLYSEL  (0x1  <<  0)  /* RW */
+#define MSDC_EMMC50_PAD_DS_TUNE_DLY2SEL (0x1  <<  1)  /* RW */
+#define MSDC_EMMC50_PAD_DS_TUNE_DLY1    (0x1F <<  2)  /* RW */
+#define MSDC_EMMC50_PAD_DS_TUNE_DLY2    (0x1F <<  7)  /* RW */
+#define MSDC_EMMC50_PAD_DS_TUNE_DLY3    (0x1F << 12)  /* RW */
+
+/* EMMC50_PAD_CMD_TUNE mask */
+#define MSDC_EMMC50_PAD_CMD_TUNE_DLY3SEL (0x1  <<  0)  /* RW */
+#define MSDC_EMMC50_PAD_CMD_TUNE_RXDLY3  (0x1F <<  1)  /* RW */
+#define MSDC_EMMC50_PAD_CMD_TUNE_TXDLY   (0x1F <<  6)  /* RW */
+
+/* EMMC50_PAD_DAT01_TUNE mask */
+#define MSDC_EMMC50_PAD_DAT0_RXDLY3SEL   (0x1  <<  0)  /* RW */
+#define MSDC_EMMC50_PAD_DAT0_RXDLY3      (0x1F <<  1)  /* RW */
+#define MSDC_EMMC50_PAD_DAT0_TXDLY       (0x1F <<  6)  /* RW */
+#define MSDC_EMMC50_PAD_DAT1_RXDLY3SEL   (0x1  << 16)  /* RW */
+#define MSDC_EMMC50_PAD_DAT1_RXDLY3      (0x1F << 17)  /* RW */
+#define MSDC_EMMC50_PAD_DAT1_TXDLY       (0x1F << 22)  /* RW */
+
+/* EMMC50_PAD_DAT23_TUNE mask */
+#define MSDC_EMMC50_PAD_DAT2_RXDLY3SEL   (0x1  <<  0)  /* RW */
+#define MSDC_EMMC50_PAD_DAT2_RXDLY3      (0x1F <<  1)  /* RW */
+#define MSDC_EMMC50_PAD_DAT2_TXDLY       (0x1F <<  6)  /* RW */
+#define MSDC_EMMC50_PAD_DAT3_RXDLY3SEL   (0x1  << 16)  /* RW */
+#define MSDC_EMMC50_PAD_DAT3_RXDLY3      (0x1F << 17)  /* RW */
+#define MSDC_EMMC50_PAD_DAT3_TXDLY       (0x1F << 22)  /* RW */
+
+/* EMMC50_PAD_DAT45_TUNE mask */
+#define MSDC_EMMC50_PAD_DAT4_RXDLY3SEL   (0x1  <<  0)  /* RW */
+#define MSDC_EMMC50_PAD_DAT4_RXDLY3      (0x1F <<  1)  /* RW */
+#define MSDC_EMMC50_PAD_DAT4_TXDLY       (0x1F <<  6)  /* RW */
+#define MSDC_EMMC50_PAD_DAT5_RXDLY3SEL   (0x1  << 16)  /* RW */
+#define MSDC_EMMC50_PAD_DAT5_RXDLY3      (0x1F << 17)  /* RW */
+#define MSDC_EMMC50_PAD_DAT5_TXDLY       (0x1F << 22)  /* RW */
+
+/* EMMC50_PAD_DAT67_TUNE mask */
+#define MSDC_EMMC50_PAD_DAT6_RXDLY3SEL   (0x1  <<  0)  /* RW */
+#define MSDC_EMMC50_PAD_DAT6_RXDLY3      (0x1F <<  1)  /* RW */
+#define MSDC_EMMC50_PAD_DAT6_TXDLY       (0x1F <<  6)  /* RW */
+#define MSDC_EMMC50_PAD_DAT7_RXDLY3SEL   (0x1  << 16)  /* RW */
+#define MSDC_EMMC50_PAD_DAT7_RXDLY3      (0x1F << 17)  /* RW */
+#define MSDC_EMMC50_PAD_DAT7_TXDLY       (0x1F << 22)  /* RW */
+
+/* EMMC51_CFG0 mask */
+#define MSDC_EMMC51_CFG_CMDQ_EN          (0x1   <<  0) /* RW !!! MT2701  Add*/
+#define MSDC_EMMC51_CFG_WDAT_CNT         (0x3FF <<  1) /* RW !!! MT2701  Add*/
+#define MSDC_EMMC51_CFG_RDAT_CNT         (0x3FF << 11) /* RW !!! MT2701  Add*/
+#define MSDC_EMMC51_CFG_CMDQ_CMD_EN      (0x1   << 21) /* RW !!! MT2701  Add*/
+
+
+/* EMMC50_CFG0 mask */
+#define MSDC_EMMC50_CFG_PADCMD_LATCHCK         (0x1  <<  0)  /* RW*/
+#define MSDC_EMMC50_CFG_CRCSTS_CNT             (0x3  <<  1)  /* RW*/
+#define MSDC_EMMC50_CFG_CRCSTS_EDGE            (0x1  <<  3)  /* RW*/
+#define MSDC_EMMC50_CFG_CRC_STS_EDGE           MSDC_EMMC50_CFG_CRCSTS_EDGE /*alias */
+
+#define MSDC_EMMC50_CFG_CRCSTS_SEL             (0x1  <<  4)  /* RW*/
+#define MSDC_EMMC50_CFG_CRC_STS_SEL            MSDC_EMMC50_CFG_CRCSTS_SEL /*alias */
+
+#define MSDC_EMMC50_CFG_ENDBIT_CHKCNT          (0xF  <<  5)  /* RW*/
+#define MSDC_EMMC50_CFG_CMDRSP_SEL             (0x1  <<  9)  /* RW*/
+#define MSDC_EMMC50_CFG_CMD_RESP_SEL           MSDC_EMMC50_CFG_CMDRSP_SEL  /*alias */
+
+#define MSDC_EMMC50_CFG_CMDEDGE_SEL            (0x1  << 10)  /* RW*/
+#define MSDC_EMMC50_CFG_ENDBIT_CNT             (0x3FF<< 11)  /* RW*/
+#define MSDC_EMMC50_CFG_READDAT_CNT            (0x7  << 21)  /* RW*/
+#define MSDC_EMMC50_CFG_EMMC50_MONSEL          (0x1  << 24)  /* RW*/
+#define MSDC_EMMC50_CFG_MSDC_WRVALID           (0x1  << 25)  /* RW*/
+#define MSDC_EMMC50_CFG_MSDC_RDVALID           (0x1  << 26)  /* RW*/
+#define MSDC_EMMC50_CFG_MSDC_WRVALID_SEL       (0x1  << 27)  /* RW*/
+#define MSDC_EMMC50_CFG_MSDC_RDVALID_SEL       (0x1  << 28)  /* RW*/
+#define MSDC_EMMC50_CFG_MSDC_TXSKEW_SEL        (0x1  << 29)  /* RW*/
+//#define MSDC_EMMC50_CFG_MSDC_GDMA_RESET      (0x1  << 31)  /* RW !!! MT2701  Removed*/
+
+/* EMMC50_CFG1 mask */
+#define MSDC_EMMC50_CFG1_WRPTR_MARGIN          (0xFF <<  0)  /* RW*/
+#define MSDC_EMMC50_CFG1_CKSWITCH_CNT          (0x7  <<  8)  /* RW*/
+#define MSDC_EMMC50_CFG1_RDDAT_STOP            (0x1  << 11)  /* RW*/
+#define MSDC_EMMC50_CFG1_WAIT8CLK_CNT          (0xF  << 12)  /* RW*/
+#define MSDC_EMMC50_CFG1_EMMC50_DBG_SEL        (0xFF << 16)  /* RW*/
+#define MSDC_EMMC50_CFG1_PSH_CNT               (0x7  << 24)  /* RW !!! MT2701  Add*/
+#define MSDC_EMMC50_CFG1_PSH_PS_SEL            (0x1  << 27)  /* RW !!! MT2701  Add*/
+#define MSDC_EMMC50_CFG1_DS_CFG                (0x1  << 28)  /* RW !!! MT2701  Add*/
+
+/* EMMC50_CFG2 mask */
+//#define MSDC_EMMC50_CFG2_AXI_GPD_UP          (0x1  <<  0)  /* RW !!! MT2701  Removed*/
+#define MSDC_EMMC50_CFG2_AXI_IOMMU_WR_EMI      (0x1  <<  1) /* RW*/
+#define MSDC_EMMC50_CFG2_AXI_SHARE_EN_WR_EMI   (0x1  <<  2) /* RW*/
+
+#define MSDC_EMMC50_CFG2_AXI_IOMMU_RD_EMI      (0x1  <<  7) /* RW*/
+#define MSDC_EMMC50_CFG2_AXI_SHARE_EN_RD_EMI   (0x1  <<  8) /* RW*/
+
+#define MSDC_EMMC50_CFG2_AXI_BOUND_128B        (0x1  << 13) /* RW*/
+#define MSDC_EMMC50_CFG2_AXI_BOUND_256B        (0x1  << 14) /* RW*/
+#define MSDC_EMMC50_CFG2_AXI_BOUND_512B        (0x1  << 15) /* RW*/
+#define MSDC_EMMC50_CFG2_AXI_BOUND_1K          (0x1  << 16) /* RW*/
+#define MSDC_EMMC50_CFG2_AXI_BOUND_2K          (0x1  << 17) /* RW*/
+#define MSDC_EMMC50_CFG2_AXI_BOUND_4K          (0x1  << 18) /* RW*/
+#define MSDC_EMMC50_CFG2_AXI_RD_OUTSTANDING_NUM (0x1F << 19) /* RW*/
+#define MSDC_EMMC50_CFG2_AXI_RD_OUTS_NUM       MSDC_EMMC50_CFG2_AXI_RD_OUTSTANDING_NUM /*alias */
+
+#define MSDC_EMMC50_CFG2_AXI_SET_LET           (0xF  << 24) /* RW*/
+#define MSDC_EMMC50_CFG2_AXI_SET_LEN           MSDC_EMMC50_CFG2_AXI_SET_LET /*alias */
+
+#define MSDC_EMMC50_CFG2_AXI_RESP_ERR_TYPE     (0x3  << 28) /* RW*/
+#define MSDC_EMMC50_CFG2_AXI_BUSY              (0x1  << 30) /* RW*/
+
+
+/* EMMC50_CFG3 mask */
+#define MSDC_EMMC50_CFG3_OUTSTANDING_WR        (0x1F <<  0) /* RW*/
+#define MSDC_EMMC50_CFG3_OUTS_WR               MSDC_EMMC50_CFG3_OUTSTANDING_WR /*alias */
+
+#define MSDC_EMMC50_CFG3_ULTRA_SET_WR          (0x3F <<  5) /* RW*/
+#define MSDC_EMMC50_CFG3_PREULTRA_SET_WR       (0x3F << 11) /* RW*/
+#define MSDC_EMMC50_CFG3_ULTRA_SET_RD          (0x3F << 17) /* RW*/
+#define MSDC_EMMC50_CFG3_PREULTRA_SET_RD       (0x3F << 23) /* RW*/
+
+/* EMMC50_CFG4 mask */
+#define MSDC_EMMC50_CFG4_IMPR_ULTRA_SET_WR     (0xFF <<  0) /* RW*/
+#define MSDC_EMMC50_CFG4_IMPR_ULTRA_SET_RD     (0xFF <<  8) /* RW*/
+#define MSDC_EMMC50_CFG4_ULTRA_EN              (0x3  << 16) /* RW*/
+#define MSDC_EMMC50_CFG4_WRAP_SEL              (0x1F << 18) /* RW !!! MT2701  Add*/
+
+/* SDC_FIFO_CFG mask */
+#define SDC_FIFO_CFG_WRVALIDSEL   (0x1 << 24)  /* RW */
+#define SDC_FIFO_CFG_RDVALIDSEL   (0x1 << 25)  /* RW */
+
+#if 1
+/* Chaotian Add GPIO top layer */
+#define MSDC_DRVN_GEAR0                       0
+#define MSDC_DRVN_GEAR1                       1
+#define MSDC_DRVN_GEAR2                       2
+#define MSDC_DRVN_GEAR3                       3
+#define MSDC_DRVN_GEAR4                       4
+#define MSDC_DRVN_GEAR5                       5
+#define MSDC_DRVN_GEAR6                       6
+#define MSDC_DRVN_GEAR7                       7
+#define MSDC_DRVN_DONT_CARE                   MSDC_DRVN_GEAR0
+
+/* for MT2712 */
+/* MSDC_CTRL0 is for MSDC0_CLK */
+#define MSDC_CTRL0          (GPIO_BASE + 0xc40)
+#define MSDC0_CLK_SMT       (0x1 << 13)
+#define MSDC0_DRV_CLK_MASK  (0x7 << 8)
+#define MSDC0_CLK_PUPD      (0x1 << 2) /* set to 1 --> pull down */
+#define MSDC0_CLK_R1R0      (0x3) /* set to 0x2 --> 50K */
+
+/* MSDC_CTRL1 is for MSDC0_CMD */
+#define MSDC_CTRL1          (GPIO_BASE + 0xc50)
+#define MSDC0_CMD_SMT       (0x1 << 13)
+#define MSDC0_DRV_CMD_MASK  (0x7 << 8)
+#define MSDC0_CMD_PUPD      (0x1 << 2) /* set to 0 --> pull up */
+#define MSDC0_CMD_R1R0      (0x3) /* set to 0x1 --> 10K */
+
+/* MSDC_CTRL2 is for MSDC0_DAT */
+#define MSDC_CTRL2          (GPIO_BASE + 0xc60)
+#define MSDC0_DAT_SMT       (0x1 << 13)
+#define MSDC0_DRV_DAT_MASK  (0x7 << 8)
+#define MSDC0_DAT_PUPD      (0x1 << 2) /* set to 0 --> pull up */
+#define MSDC0_DAT_R1R0      (0x3) /* set to 0x1 --> 10K */
+
+/* MSDC_CTRL4 is for MSDC0_DS */
+#define MSDC_CTRL4          (GPIO_BASE + 0xda0)
+#define MSDC0_DS_SMT        (0x1 << 13)
+#define MSDC0_DRV_DS_MASK   (0x7 << 8)
+#define MSDC0_DS_PUPD       (0x1 << 2) /* set to 1 --> pull down */
+#define MSDC0_DS_R1R0       (0x3) /* set to 0x2 --> 50K */
+
+/* for GPIO67 contrl */
+#define GPIO_MODE14 (GPIO_BASE + 0x5d0) /*0x7 << 6 --> GPIO67 mode */
+#define GPIO_DIR5   (GPIO_BASE + 0x40) /* bit3 --> GPIO67 DIR, b1 to output */
+#define GPIO_DOUT5  (GPIO_BASE + 0x340) /* bit3 -->GPIO67 output value, b1 to high */
+#endif
+
+typedef enum __MSDC_PIN_STATE {
+    MSDC_HIGHZ = 0,
+    MSDC_10KOHM,
+    MSDC_50KOHM,
+    MSDC_8KOHM,
+    MSDC_PST_MAX
+} MSDC_PIN_STATE;
+
+
+/* each PLL have different gears for select
+ * software can used mux interface from clock management module to select */
+enum {
+    MSDC50_CLKSRC4HCLK_26MHZ  = 0,
+    MSDC50_CLKSRC4HCLK_273MHZ,
+    MSDC50_CLKSRC4HCLK_182MHZ,
+    MSDC50_CLKSRC4HCLK_78MHZ,
+    MSDC_DONOTCARE_HCLK,
+    MSDC50_CLKSRC4HCLK_MAX
+};
+
+enum {
+    MSDC50_CLKSRC_26MHZ  = 0,
+    MSDC50_CLKSRC_400MHZ,  /* MSDCPLL_CK */
+    MSDC50_CLKSRC_182MHZ,  /*MSDCPLL_D2 */
+    MSDC50_CLKSRC_136MHZ,
+    MSDC50_CLKSRC_156MHZ,
+    MSDC50_CLKSRC_200MHZ,  /*MSDCPLL_D4 */
+    MSDC50_CLKSRC_100MHZ,
+    MSDC50_CLKSRC_50MHZ,
+    MSDC50_CLKSRC_MAX
+};
+
+/* MSDC0/1/2
+     PLL MUX SEL List */
+enum {
+    MSDC30_CLKSRC_26MHZ   = 0,
+    MSDC30_CLKSRC_200MHZ,
+    MSDC30_CLKSRC_182MHZ,
+    MSDC30_CLKSRC_91MHZ,
+    MSDC30_CLKSRC_156MHZ,
+    MSDC30_CLKSRC_104MHZ,
+    MSDC30_CLKSRC_MAX
+};
+
+#define MSDC50_CLKSRC_DEFAULT     MSDC50_CLKSRC_400MHZ
+#define MSDC30_CLKSRC_DEFAULT     MSDC30_CLKSRC_200MHZ
+
+typedef enum MT65XX_POWER_VOL_TAG {
+    VOL_DEFAULT,
+    VOL_0900 = 900,
+    VOL_1000 = 1000,
+    VOL_1100 = 1100,
+    VOL_1200 = 1200,
+    VOL_1300 = 1300,
+    VOL_1350 = 1350,
+    VOL_1500 = 1500,
+    VOL_1800 = 1800,
+    VOL_2000 = 2000,
+    VOL_2100 = 2100,
+    VOL_2500 = 2500,
+    VOL_2800 = 2800,
+    VOL_3000 = 3000,
+    VOL_3300 = 3300,
+    VOL_3400 = 3400,
+    VOL_3500 = 3500,
+    VOL_3600 = 3600
+} MT65XX_POWER_VOLTAGE;
+
+/*--------------------------------------------------------------------------*/
+/* Descriptor Structure                                                     */
+/*--------------------------------------------------------------------------*/
+#define DMA_FLAG_NONE       (0x00000000)
+#define DMA_FLAG_EN_CHKSUM  (0x00000001)
+#define DMA_FLAG_PAD_BLOCK  (0x00000002)
+#define DMA_FLAG_PAD_DWORD  (0x00000004)
+
+#define MSDC_WRITE32(addr, data)    writel(data, addr)
+#define MSDC_READ32(addr)           readl(addr)
+#define MSDC_WRITE8(addr, data)     writeb(data, addr)
+#define MSDC_READ8(addr)            readb(addr)
+
+#define MSDC_SET_BIT32(addr,mask) \
+    do {    \
+        unsigned int tv = MSDC_READ32(addr); \
+        tv |=((u32)(mask)); \
+        MSDC_WRITE32(addr,tv); \
+    } while (0)
+#define MSDC_CLR_BIT32(addr,mask) \
+    do {    \
+        unsigned int tv = MSDC_READ32(addr); \
+        tv &= ~((u32)(mask)); \
+        MSDC_WRITE32(addr,tv); \
+    } while (0)
+
+#define MSDC_SET_FIELD(reg,field,val) \
+    do { \
+        u32 tv = MSDC_READ32(reg); \
+        tv &= ~((u32)(field)); \
+        tv |= ((val) << (__builtin_ffs((u32)(field)) - 1)); \
+        MSDC_WRITE32(reg, tv); \
+    } while (0)
+
+#define MSDC_GET_FIELD(reg,field,val) \
+    do { \
+        u32 tv = MSDC_READ32(reg); \
+        val = ((tv & (field)) >> (__builtin_ffs((u32)(field)) - 1)); \
+    } while (0)
+
+#define MSDC_RETRY(expr,retry,cnt) \
+    do { \
+        uint32_t t = cnt; \
+        uint32_t r = retry; \
+        uint32_t c = cnt; \
+        while (r) { \
+            if (!(expr)) break; \
+            if (c-- == 0) { \
+                r--; spin(200); c = t; \
+            } \
+        } \
+        if (r == 0) \
+            dprintf(CRITICAL, "%s->%d: retry %d times failed!\n", __func__, \
+                    __LINE__, retry); \
+    } while (0)
+
+#define MSDC_RESET() \
+    do { \
+        MSDC_SET_BIT32(MSDC_CFG, MSDC_CFG_RST); \
+        MSDC_RETRY(MSDC_READ32(MSDC_CFG) & MSDC_CFG_RST, 5, 1000); \
+    } while (0)
+
+#define MSDC_CLR_INT() \
+    do { \
+        volatile uint32_t val = MSDC_READ32(MSDC_INT); \
+        MSDC_WRITE32(MSDC_INT, val); \
+    } while (0)
+
+#define MSDC_CLR_FIFO() \
+    do { \
+        MSDC_SET_BIT32(MSDC_FIFOCS, MSDC_FIFOCS_CLR); \
+        MSDC_RETRY(MSDC_READ32(MSDC_FIFOCS) & MSDC_FIFOCS_CLR, 5, 1000); \
+    } while (0)
+
+#define MSDC_FIFO_WRITE32(val)  MSDC_WRITE32(MSDC_TXDATA, val)
+#define MSDC_FIFO_READ32()      MSDC_READ32(MSDC_RXDATA)
+#define MSDC_FIFO_WRITE8(val)   MSDC_WRITE8(MSDC_TXDATA, val)
+#define MSDC_FIFO_READ8()       MSDC_READ8(MSDC_RXDATA)
+
+#define MSDC_TXFIFOCNT() \
+    ((MSDC_READ32(MSDC_FIFOCS) & MSDC_FIFOCS_TXCNT) >> 16)
+#define MSDC_RXFIFOCNT() \
+    ((MSDC_READ32(MSDC_FIFOCS) & MSDC_FIFOCS_RXCNT) >> 0)
+
+#define SDC_IS_BUSY()       (MSDC_READ32(SDC_STS) & SDC_STS_SDCBUSY)
+#define SDC_IS_CMD_BUSY()   (MSDC_READ32(SDC_STS) & SDC_STS_CMDBUSY)
+
+#define SDC_SEND_CMD(cmd,arg) \
+    do { \
+        MSDC_WRITE32(SDC_ARG, (arg)); \
+        MSDC_WRITE32(SDC_CMD, (cmd)); \
+    } while (0)
+
+#define MSDC_DMA_ON     MSDC_CLR_BIT32(MSDC_CFG, MSDC_CFG_PIO);
+#define MSDC_DMA_OFF    MSDC_SET_BIT32(MSDC_CFG, MSDC_CFG_PIO);
+
+#define MSDC_RELIABLE_WRITE     (0x1 << 0)
+#define MSDC_PACKED             (0x1 << 1)
+#define MSDC_TAG_REQUEST        (0x1 << 2)
+#define MSDC_CONTEXT_ID         (0x1 << 3)
+#define MSDC_FORCED_PROG        (0x1 << 4)
+
+#ifdef FPGA_PLATFORM
+#define msdc_pin_set(...)
+#define msdc_set_smt(...)
+#define msdc_set_driving(...)
+#endif
+
+int msdc_init(struct mmc_host *host);
+void msdc_config_bus(struct mmc_host *host, u32 width);
+int msdc_dma_transfer(struct mmc_host *host, struct mmc_data *data);
+int msdc_tune_bwrite(struct mmc_host *host, u32 dst, u8 *src, u32 nblks);
+int msdc_tune_bread(struct mmc_host *host, u8 *dst, u32 src, u32 nblks);
+void msdc_reset_tune_counter(struct mmc_host *host);
+int msdc_abort_handler(struct mmc_host *host, int abort_card);
+int msdc_tune_read(struct mmc_host *host);
+void msdc_config_clock(struct mmc_host *host, int state, u32 hz);
+int msdc_cmd(struct mmc_host *host, struct mmc_command *cmd);
+void msdc_set_timeout(struct mmc_host *host, u32 ns, u32 clks);
+void msdc_set_autocmd(struct mmc_host *host, int cmd);
+int msdc_get_autocmd(struct mmc_host *host);
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/msdc_cfg.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/msdc_cfg.h
new file mode 100644
index 0000000..0afd634
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/msdc_cfg.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#pragma once
+
+/*--------------------------------------------------------------------------*/
+/* Common Definition                                                        */
+/*--------------------------------------------------------------------------*/
+#ifdef MACH_FPGA
+#define FPGA_PLATFORM
+#endif
+
+/* HW deal with the 2K DMA boundary limitation, SW do nothing with it */
+/* Most of eMMC request in lk are sequential access, so it's no need to
+ * use descript DMA mode, I just remove relevant codes. JieWu@20160607 */
+#define MSDC_USE_DMA_MODE
+
+#define FEATURE_MMC_WR_TUNING
+#define FEATURE_MMC_RD_TUNING
+#define FEATURE_MMC_CM_TUNING
+
+/* Maybe we discard these macro definition */
+//#define MSDC_USE_PATCH_BIT2_TURNING_WITH_ASYNC
+
+/*--------------------------------------------------------------------------*/
+/* Debug Definition                                                         */
+/*--------------------------------------------------------------------------*/
+//#define KEEP_SLIENT_BUILD
+//#define ___MSDC_DEBUG___
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/mt2712.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mt2712.h
new file mode 100644
index 0000000..64f5295
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mt2712.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#pragma once
+#include <compiler.h>
+#include <debug.h>
+
+#if LK_AS_BL33 == 0 /* LK as BL2 */
+/* program memory: L2C for BL2 */
+#define MEMORY_BASE_PHYS        (0x200000)
+#define MEMORY_APERTURE_SIZE    (0x40000UL)
+
+/* internal SRAM */
+#define SRAM_BASE_PHYS          (0x100000)
+#define SRAM_BASE_SIZE          (0x30000UL)
+
+/* memory for log store(part of internal sram) */
+#define MEMORY_LOG_PHYS         (SRAM_BASE_PHYS + SRAM_BASE_SIZE - \
+                                 SRAM_LOG_SIZE - MRDUMP_CB_SIZE)
+#define MEMORY_LOG_SIZE         (SRAM_LOG_SIZE)
+
+#else /* LK as BL33 */
+
+/* program memory and memory before mempool */
+#define MEMORY_BASE_PHYS        (0x40000000)
+#define MEMORY_APERTURE_SIZE    (0x33600000UL)
+
+/* non-secure accessible internal SRAM region */
+#define SRAM_BASE_PHYS          (0x118000)
+#define SRAM_BASE_SIZE          (0x18000UL)
+
+/* memory for log store(dram region) */
+#define MEMORY_LOG_PHYS         (0x43EC0000)
+#define MEMORY_LOG_SIZE         (0x40000)
+
+#endif
+
+/* cmsys sram */
+#define CMSYS_SRAM_PHYS         (0x130000)
+#define CMSYS_SRAM_SIZE         (0x20000UL)
+
+/* chip id, hw/sw version */
+#define CHIP_ID_BASE_PHYS       (0x08000000)
+#define CHIP_ID_BASE_SIZE       (0x200000)
+
+/* peripheral */
+#define PERIPHERAL_BASE_PHYS    (0x10000000)
+#define PERIPHERAL_BASE_SIZE    (0x10000000UL)
+
+/* dram */
+#define DRAM_BASE_PHY           (0x40000000UL)
+
+#if WITH_KERNEL_VM
+#define MEMORY_BASE_VIRT        (KERNEL_ASPACE_BASE + MEMORY_BASE_PHYS)
+#define SRAM_BASE_VIRT          (KERNEL_ASPACE_BASE + 0x300000)
+#define CMSYS_SRAM_VIRT         (KERNEL_ASPACE_BASE + CMSYS_SRAM_PHYS)
+#define CHIP_ID_BASE_VIRT       (KERNEL_ASPACE_BASE + CHIP_ID_BASE_PHYS)
+#define PERIPHERAL_BASE_VIRT    (KERNEL_ASPACE_BASE + PERIPHERAL_BASE_PHYS)
+#define DRAM_BASE_VIRT          (KERNEL_ASPACE_BASE + DRAM_BASE_PHY)
+
+#else
+#define MEMORY_BASE_VIRT        MEMORY_BASE_PHYS
+#define SRAM_BASE_VIRT          SRAM_BASE_PHYS
+#define CMSYS_SRAM_VIRT         CMSYS_SRAM_PHYS
+#define CHIP_ID_BASE_VIRT       CHIP_ID_BASE_PHYS
+#define PERIPHERAL_BASE_VIRT    PERIPHERAL_BASE_PHYS
+#define DRAM_BASE_VIRT          DRAM_BASE_PHY
+#endif
+
+/* LK/BL33 heap phy address */
+#define HEAP_BASE_PHY           0x734d0000UL
+#define HEAP_BASE_SIZE          0x30000
+
+/* |--176KB ARENA--|--8KB Log Buffer--|--8KB mrdump--|*/
+/* reserve 8KB sram for mrdump(last 8KB) */
+#define MRDUMP_CB_SIZE          0x2000
+/* reserve 8KB sram for log before dram init(only in bl2) */
+#define SRAM_LOG_SIZE           0x2000
+/* sram used as arena memory*/
+#define SRAM_ARENA_BASE         SRAM_BASE_PHYS
+#define SRAM_ARENA_SIZE         (SRAM_BASE_SIZE - MRDUMP_CB_SIZE - \
+                                 SRAM_LOG_SIZE)
+
+#define DRAM_ARENA_BASE         HEAP_BASE_PHY
+#define DRAM_ARENA_SIZE         HEAP_BASE_SIZE
+
+/* 256KB dram to store bl2/bl33 log */
+#define DRAM_LOG_ADDR           0x78032000
+#define DRAM_LOG_SIZE           0x40000
+
+/* 4KB use for LK2.0 transfer bootargs to BL33 */
+#define DRAM_BOOTARG_OFFSET     0x334cf000
+#define DRAM_BOOTARG_BASE       (DRAM_BASE_PHY + DRAM_BOOTARG_OFFSET)
+#define DRAM_BOOTARG_SIZE       0x1000
+
+/* interrupts */
+#define ARM_GENERIC_TIMER_VIRTUAL_INT 27
+#define ARM_GENERIC_TIMER_PHYSICAL_INT 30
+
+#define MAX_INT 292
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/mt_gpio.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mt_gpio.h
new file mode 100644
index 0000000..bf0b66e
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mt_gpio.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#pragma once
+
+#define GPIO_RDSEL0_EN    (GPIO_BASE + 0xA10)
+#define GPIO_RDSEL1_EN    (GPIO_BASE + 0xA20)
+#define GPIO_RDSEL2_EN    (GPIO_BASE + 0xA30)
+#define GPIO_RDSEL3_EN    (GPIO_BASE + 0xA40)
+#define GPIO_RDSEL4_EN    (GPIO_BASE + 0xA50)
+#define GPIO_RDSEL5_EN    (GPIO_BASE + 0xA60)
+#define GPIO_RDSEL6_EN    (GPIO_BASE + 0xA70)
+#define GPIO_RDSEL7_EN    (GPIO_BASE + 0xA80)
+#define GPIO_RDSEL8_EN    (GPIO_BASE + 0xA90)
+#define GPIO_RDSEL9_EN    (GPIO_BASE + 0xAA0)
+#define GPIO_RDSELA_EN    (GPIO_BASE + 0xAB0)
+#define GPIO_RDSELB_EN    (GPIO_BASE + 0xAC0)
+#define GPIO_RDSELC_EN    (GPIO_BASE + 0xAD0)
+#define GPIO_RDSELD_EN    (GPIO_BASE + 0xAE0)
+#define GPIO_RDSELE_EN    (GPIO_BASE + 0xAF0)
+#define GPIO_RDSELF_EN    (GPIO_BASE + 0xB00)
+#define GPIO_RDSEL10_EN   (GPIO_BASE + 0xB10)
+#define GPIO_RDSEL11_EN   (GPIO_BASE + 0xB20)
+#define GPIO_MSDC0_CTRL5  (GPIO_BASE + 0xC70)
+#define GPIO_MSDC1_CTRL5  (GPIO_BASE + 0xCB0)
+#define GPIO_MSDC2_CTRL5  (GPIO_BASE + 0xD10)
+#define GPIO_MSDC3_CTRL5  (GPIO_BASE + 0xD70)
+#define GPIO_EXMD_CTRL0   (GPIO_BASE + 0xE40)
+#define GPIO_NC_CTRL4     (GPIO_BASE + 0xF60)
+
+void mt_gpio_init(void);
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/mt_infracfg.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mt_infracfg.h
new file mode 100644
index 0000000..eb7a287
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mt_infracfg.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2017 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#pragma once
+
+#include <platform/mt2712.h>
+
+#define TOPAXI_PROT_EN      (INFRACFG_BASE + 0x220)
+#define TOPAXI_PROT_STA1    (INFRACFG_BASE + 0x228)
+#define TOPAXI_PROT_EN1     (INFRACFG_BASE + 0x250)
+#define TOPAXI_PROT_STA3    (INFRACFG_BASE + 0x258)
+#define TOPAXI_PROT_EN_SET  (INFRACFG_BASE + 0x260)
+#define TOPAXI_PROT_EN_CLR  (INFRACFG_BASE + 0x264)
+#define INFRA_MISC          (INFRACFG_BASE + 0xf00)
+
+/* TOPAXI_PROT_EN_SET and TOPAXI_PROT_EN_CLR setting */
+#define MFG_PROT_MASK       ((0x1 << 14) | (0x1 << 21) | (0x1 << 23))
+
+/* INFRA_MISC setting */
+#define DDR_4GB_SUPPORT_EN  (0x1 << 13)
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/mt_irq.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mt_irq.h
new file mode 100644
index 0000000..3c23f0e
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mt_irq.h
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#pragma once
+
+#define GIC_DIST_PENDING_SET            0x200
+#define GIC_DIST_ENABLE_SET             0x100
+#define GIC_DIST_ENABLE_CLEAR           0x180
+#define GIC_DIST_PENDING_CLEAR          0x280
+#define GIC_DIST_CTR                    0x004
+#define GIC_DIST_CTRL                   0x000
+#define GIC_DIST_TARGET                 0x800
+#define GIC_DIST_ACTIVE_BIT             0x300
+#define GIC_DIST_PRI                    0x400
+#define GIC_DIST_ICDISR                 0x80
+#define GIC_DIST_CONFIG                 0xc00
+#define GIC_DIST_SOFTINT                0xf00
+
+#define GIC_CPU_HIGHPRI                 0x18
+#define GIC_CPU_EOI                     0x10
+#define GIC_CPU_RUNNINGPRI              0x14
+#define GIC_CPU_BINPOINT                0x08
+#define GIC_CPU_INTACK                  0x0c
+#define GIC_CPU_CTRL                    0x00
+#define GIC_CPU_PRIMASK                 0x04
+
+/*
+ * Define IRQ code.
+ */
+#define GIC_PRIVATE_SIGNALS (32)
+
+#define GIC_PPI_OFFSET          (27)
+#define GIC_PPI_GLOBAL_TIMER    (GIC_PPI_OFFSET + 0)
+#define GIC_PPI_LEGACY_FIQ      (GIC_PPI_OFFSET + 1)
+#define GIC_PPI_PRIVATE_TIMER   (GIC_PPI_OFFSET + 2)
+#define GIC_PPI_WATCHDOG_TIMER  (GIC_PPI_OFFSET + 3)
+#define GIC_PPI_LEGACY_IRQ      (GIC_PPI_OFFSET + 4)
+
+#define USB_MCU_IRQ_BIT0_ID          104
+#define USB_MCU_IRQ_BIT1_ID          105
+#define TS_IRQ_BIT_ID                106
+#define TS_BATCH_IRQ_BIT_ID          107
+#define LOWBATTERY_IRQ_BIT_ID        108
+#define PWM_IRQ_BIT_ID               109
+#define THERM_CTRL_IRQ_BIT_ID        110
+#define MSDC0_IRQ_BIT_ID             111
+#define MSDC1_IRQ_BIT_ID             112
+#define MSDC2_IRQ_BIT_ID             113
+#define MSDC3_IRQ_BIT_ID             114
+#define I2C0_IRQ_BIT_ID              116
+#define I2C1_IRQ_BIT_ID              117
+#define I2C2_IRQ_BIT_ID              118
+#define I2C3_IRQ_BIT_ID              119
+#define I2C4_IRQ_BIT_ID              120
+#define I2C6_IRQ_BIT_ID              122
+#define UART0_IRQ_BIT_ID             123
+#define UART1_IRQ_BIT_ID             124
+#define UART2_IRQ_BIT_ID             125
+#define UART3_IRQ_BIT_ID             126
+#define NFIECC_IRQ_BIT_ID            127
+#define NFI_IRQ_BIT_ID               128
+#define AP_DMA_IRDA0_IRQ_BIT_ID      129
+#define AP_DMA_I2C0_IRQ_BIT_ID       130
+#define AP_DMA_I2C1_IRQ_BIT_ID       131
+#define AP_DMA_I2C2_IRQ_BIT_ID       132
+#define AP_DMA_I2C3_IRQ_BIT_ID       133
+#define AP_DMA_I2C4_IRQ_BIT_ID       134
+#define AP_DMA_UART0_TX_IRQ_BIT_ID   135
+#define AP_DMA_UART0_RX_IRQ_BIT_ID   136
+#define AP_DMA_UART1_TX_IRQ_BIT_ID   137
+#define AP_DMA_UART1_RX_IRQ_BIT_ID   138
+#define AP_DMA_UART2_TX_IRQ_BIT_ID   139
+#define AP_DMA_UART2_RX_IRQ_BIT_ID   140
+#define AP_DMA_UART3_TX_IRQ_BIT_ID   141
+#define AP_DMA_UART3_RX_IRQ_BIT_ID   142
+#define AP_DMA_UART4_TX_IRQ_BIT_ID   143
+#define AP_DMA_UART4_RX_IRQ_BIT_ID   144
+#define AP_DMA_UART5_TX_IRQ_BIT_ID   145
+#define AP_DMA_UART5_RX_IRQ_BIT_ID   146
+#define PE2_MAC_IRQ_P0_BIT_ID        147
+#define IRDA_IRQ_BIT_ID              148
+#define PE2_MAC_IRQ_P1_BIT_ID        149
+#define SPI0_IRQ_BIT_ID              150
+#define MSDC0_WAKEUP_PS_IRQ_BIT_ID   151
+#define MSDC1_WAKEUP_PS_IRQ_BIT_ID   152
+#define MSDC2_WAKEUP_PS_IRQ_BIT_ID   153
+#define SSUSB_DEV_INT_ID             154
+#define SSUSB_XHCI_INT_B_ID          155
+#define MSDC3_WAKEUP_PS_IRQ_BIT_ID   156
+#define PTP_FSM_IRQ_BIT_ID           157
+#define UART4_IRQ_BIT_ID             158
+#define UART5_IRQ_BIT_ID             159
+#define WDT_IRQ_BIT_ID               160
+#define DCC_APARM_IRQ_BIT_ID         164
+#define BUS_DBG_TRACKER_IRQ_BIT_ID   165
+#define APARM_DOMAIN_IRQ_BIT_ID      166
+#define APARM_DECERR_IRQ_BIT_ID      167
+#define DOMAIN_ABORT_IRQ_BIT0_ID     168
+#define APMIXEDSYS_TX_IRQ_ID         169
+#define MIPI_CSI_TX_IRQ_ID           170
+#define MIPI_DSI_TX_IRQ_ID           171
+#define INFRA_APB_ASYNC_IRQ_ID       172
+#define TRNG_IRQ_BIT_ID              173
+#define AFE_MCU_IRQ_BIT_ID           174
+#define CQ_DMA_IRQ_BIT_ID            175
+#define CQ_DMA_SEC_IRQ_BIT_ID        176
+#define MM2_IOMMU_IRQ_B_ID           177
+#define MM2_IOMMU_SEC_IRQ_B_ID       178
+#define MM1_IOMMU_IRQ_B_ID           179
+#define MM1_IOMMU_SEC_IRQ_B_ID       180
+#define REFRESH_RATE_IRQ_BIT_ID      181
+#define GCPU_IRQ_BIT_ID              182
+#define GCPU_DMX_IRQ_BIT_ID          183
+#define APXGPT_IRQ_BIT_ID            184
+#define EINT_IRQ_BIT0_ID             185
+#define EINT_IRQ_BIT1_ID             186
+#define EINT_EVENT_IRQ_BIT0_ID       187
+#define EINT_EVENT_IRQ_BIT1_ID       188
+#define EINT_DIRECT_IRQ_BIT0_ID      189
+#define EINT_DIRECT_IRQ_BIT1_ID      190
+#define EINT_DIRECT_IRQ_BIT2_ID      191
+#define EINT_DIRECT_IRQ_BIT3_ID      192
+#define IRRX_IRQ_BIT_ID              193
+#define KP_IRQ_BIT_ID                194
+#define SLEEP_IRQ_BIT0_ID            195
+#define SLEEP_IRQ_BIT1_ID            196
+#define SLEEP_IRQ_BIT2_ID            197
+#define SLEEP_IRQ_BIT3_ID            198
+#define SLEEP_IRQ_BIT4_ID            199
+#define SLEEP_IRQ_BIT5_ID            200
+#define SLEEP_IRQ_BIT6_ID            201
+#define SLEEP_IRQ_BIT7_ID            202
+#define SEJ_APXGPT_IRQ_BIT_ID        203
+#define SEJ_WDT_IRQ_BIT_ID           204
+#define SYS_TIMER_IRQ_BIT0_ID        208
+#define MM_MUTEX_IRQ_BIT_ID          209
+#define MDP_RDMA0_IRQ_BIT_ID         210
+#define MDP_RDMA1_IRQ_BIT_ID         211
+#define MDP_RSZ0_IRQ_BIT_ID          212
+#define MDP_RSZ1_IRQ_BIT_ID          213
+#define MDP_RSZ2_IRQ_BIT_ID          214
+#define MDP_TDSHP0_IRQ_BIT_ID        215
+#define MDP_TDSHP1_IRQ_BIT_ID        216
+#define MDP_WDMA_IRQ_BIT_ID          217
+#define MDP_WROT0_IRQ_BIT_ID         218
+#define MDP_WROT1_IRQ_BIT_ID         219
+#define DISP_OVL0_IRQ_BIT_ID         220
+#define DISP_OVL1_IRQ_BIT_ID         221
+#define DISP_RDMA0_IRQ_BIT_ID        222
+#define DISP_RDMA1_IRQ_BIT_ID        223
+#define DISP_RDMA2_IRQ_BIT_ID        224
+#define DISP_WDMA0_IRQ_BIT_ID        225
+#define DISP_WDMA1_IRQ_BIT_ID        226
+#define DISP_COLOR0_IRQ_BIT_ID       227
+#define DISP_COLOR1_IRQ_BIT_ID       228
+#define DISP_AAL_IRQ_BIT_ID          229
+#define DISP_GAMMA_IRQ_BIT_ID        230
+#define DISP_UFOE_IRQ_BIT_ID         231
+#define DSI0_IRQ_BIT_ID              232
+#define DSI1_IRQ_BIT_ID              233
+#define DPI0_IRQ_BIT_ID              234
+#define MJC_APB_ERR_IRQ_BIT_ID       235
+#define DISP_OD_IRQ_BIT_ID           236
+#define DPI1_IRQ_BIT_ID              237
+#define VENC_IRQ_BIT_ID              238
+#define SMI_LARB3_IRQ_BIT_ID         239
+#define JPGDEC1_IRQ_BIT_ID           240
+#define VEN2_IRQ_BIT_ID              242
+#define JPGDEC_IRQ_BIT_ID            243
+#define VDEC_IRQ_BIT_ID              244
+#define SMI_LARB1_IRQ_BIT_ID         245
+#define IMG_RESZ_IRQ_BIT_ID          246
+#define SMI_LARB0_IRQ_BIT_ID         247
+#define SMI_LARB4_IRQ_BIT_ID         248
+#define SMI_LARB2_IRQ_BIT_ID         249
+#define SENINF_IRQ_BIT_ID            250
+#define CAM0_IRQ_BIT_ID              251
+#define CAM1_IRQ_BIT_ID              252
+#define CAM2_IRQ_BIT_ID              253
+#define CAM_SV0_IRQ_BIT_ID           254
+#define CAM_SV1_IRQ_BIT_ID           255
+#define FD_IRQ_BIT_ID                256
+#define MFG_IRQ_BIT0_ID              257
+#define MFG_IRQ_BIT1_ID              258
+#define MFG_IRQ_BIT2_ID              259
+#define MFG_IRQ_BIT3_ID              260
+#define MFG_IRQ_BIT4_ID              261
+#define MFG_IRQ_BIT5_ID              262
+#define MFG_IRQ_BIT6_ID              263
+#define MFG_IRQ_BIT7_ID              264
+#define MD_WDT_IRQ_BIT_ID            265
+#define CLDMA_AP_IRQ_BIT_ID          266
+#define AP2MD_BUS_TIMEOUT_IRQ_BIT_ID 267
+#define SMI_LARB6_IRQ_BIT_ID         268
+#define MT_NOR_IRQ_ID                270
+#define RTC_IRQ_BIT_ID               271
+#define SMI_LARB7_IRQ_BIT_ID         272
+#define SMI_LARB5_IRQ_BIT_ID         273
+#define ASYS_MCU_IRQ_BIT_ID          274
+#define ASRC1_MCU_IRQ_BIT_ID         275
+#define ASRC2_MCU_IRQ_BIT_ID         276
+#define ASRC3_MCU_IRQ_BIT_ID         277
+#define ASRC4_MCU_IRQ_BIT_ID         278
+#define ASRC5_MCU_IRQ_BIT_ID         279
+#define SYS_TIMER_IRQ_BIT1_ID        284
+#define SYS_TIMER_IRQ_BIT2_ID        285
+#define SYS_TIMER_IRQ_BIT3_ID        286
+#define SYS_TIMER_IRQ_BIT4_ID        287
+#define SYS_TIMER_IRQ_BIT5_ID        288
+#define MDP_RDMA2_IRQ_BIT_ID         289
+#define MDP_RDMA3_IRQ_BIT_ID         290
+#define MDP_WROT2_IRQ_BIT_ID         291
+#define DISP_AAL1_IRQ_BIT_ID         292
+#define DISP_OD1_IRQ_BIT_ID          293
+#define DISP_OVL2_IRQ_BIT_ID         294
+#define DISP_COLOR2_IRQ_BIT_ID       295
+#define DISP_WDMA2_IRQ_BIT_ID        296
+#define MDP_TDSHP2_IRQ_BIT_ID        297
+#define DSI2_IRQ_BIT_ID              298
+#define DSI3_IRQ_BIT_ID              299
+#define CAM_SV2_IRQ_BIT_ID           300
+#define CAM_SV3_IRQ_BIT_ID           301
+#define CAM_SV4_IRQ_BIT_ID           302
+#define CAM_SV5_IRQ_BIT_ID           303
+#define DISPFMT_VSYNC_IRQ_BIT_ID     304
+#define VDO_DISP_END_IRQ_BIT_ID      305
+#define VDO_UNDER_RUN_IRQ_BIT_ID     306
+#define WR_CHANNEL_DI_IRQ_BIT_ID     307
+#define WR_CHANNEL_VDI_IRQ_BIT_ID    308
+#define NR_IRQ_BIT_ID                309
+#define VDOIN_IRQ_BIT_ID             310
+#define DUMMY0_IRQ_BIT_ID            311
+#define DUMMY1_IRQ_BIT_ID            312
+#define DUMMY2_IRQ_BIT_ID            313
+#define DUMMY3_IRQ_BIT_ID            314
+#define SPI1_IRQ_BIT_ID              315
+#define SPI2_IRQ_BIT_ID              316
+#define SPI3_IRQ_BIT_ID              317
+#define SPI4_IRQ_BIT_ID              318
+#define SPI5_IRQ_BIT_ID              319
+#define GCPU_MMU_IRQ_BIT_ID          320
+#define GCPU_MMU_SEC_IRQ_BIT_ID      321
+#define SRAM_ECC_ERR_IRQ_BIT_ID      322
+
+#define MT_NR_PPI   (5)
+#define MT_NR_SPI   (292)
+#define NR_IRQ_LINE  (GIC_PPI_OFFSET + MT_NR_PPI + MT_NR_SPI)
+
+#define EDGE_SENSITIVE 0
+#define LEVEL_SENSITIVE 1
+
+#define MT65xx_POLARITY_LOW   0
+#define MT65xx_POLARITY_HIGH  1
+
+void mt_irq_set_sens(unsigned int irq, unsigned int sens);
+void mt_irq_set_polarity(unsigned int irq, unsigned int polarity);
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/mt_pericfg.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mt_pericfg.h
new file mode 100644
index 0000000..053f979
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mt_pericfg.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#pragma once
+
+#include <platform/mt2712.h>
+
+#define PERIAXI_BUS_CTL3    (PERICFG_BASE + 0x208)
+#define PERISYS_4G_SUPPORT  (0x1 << 11)
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/mt_reg_base.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mt_reg_base.h
new file mode 100644
index 0000000..0abe9bc
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mt_reg_base.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#pragma once
+
+#include <platform/mt2712.h>
+
+/* I/O mapping */
+#define IO_PHYS             PERIPHERAL_BASE_VIRT
+#define IO_SIZE             PERIPHERAL_BASE_SIZE
+
+#define CKSYS_BASE          IO_PHYS
+#define INFRACFG_BASE       (IO_PHYS + 0x00001000)
+#define SCP_BASE_CFG        (IO_PHYS + 0x00002000)
+#define PERICFG_BASE        (IO_PHYS + 0x00003000)
+/* IO register definitions */
+#define GPIO_BASE           (IO_PHYS + 0x00005000)
+#define SEJ_BASE            (IO_PHYS + 0x0000A000)
+#define SPM_BASE            (IO_PHYS + 0x00006000)
+#define TOP_RGU_BASE        (IO_PHYS + 0x00007000)
+#define APMIXED_BASE        (IO_PHYS + 0x00209000)
+#define GIC_CPU_BASE        (IO_PHYS + 0x00520000)
+#define GIC_DIST_BASE       (IO_PHYS + 0x00510000)
+#define MCUSYS_CFGREG_BASE  (IO_PHYS + 0x00220000)
+#define INT_POL_CTL0        (MCUSYS_CFGREG_BASE + 0xa80)
+
+#define UART0_BASE          (IO_PHYS + 0x01002000)
+#define UART1_BASE          (IO_PHYS + 0x01003000)
+#define UART2_BASE          (IO_PHYS + 0x01004000)
+#define UART3_BASE          (IO_PHYS + 0x01005000)
+#define MSDC0_BASE          (IO_PHYS + 0x01230000)
+#define MSDC1_BASE          (IO_PHYS + 0x01240000)
+#define NOR_BASE            (IO_PHYS + 0x0100D000)
+#define NFI_BASE            (IO_PHYS + 0x0100E000)
+#define NFIECC_BASE         (IO_PHYS + 0x0100F000)
+
+#define APMIXED_BASE        (IO_PHYS + 0x00209000)
+#define TRNG_BASE           (IO_PHYS + 0x0020F000)
+
+/* APB Module ssusb_top */
+#define USB3_BASE           (IO_PHYS + 0x01270000)
+#define USB3_SIF_BASE       (IO_PHYS + 0x01280000)
+#define USB3_IPPC_BASE      (IO_PHYS + 0x01280700)
+#define USB3_SIF2_BASE      (IO_PHYS + 0x01290000)
+
+#define MFGCFG_BASE         (IO_PHYS + 0x03000000)
+#define MMSYS_BASE          (IO_PHYS + 0x04000000)
+#define IMGSYS_BASE         (IO_PHYS + 0x05000000)
+#define BDPSYS_BASE         (IO_PHYS + 0x05010000)
+#define VDECSYS_BASE        (IO_PHYS + 0x06000000)
+#define VENCSYS_BASE        (IO_PHYS + 0x08000000)
+#define JPGDECSYS_BASE      (IO_PHYS + 0x09000000)
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/mt_scp.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mt_scp.h
new file mode 100644
index 0000000..2aef6d8
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mt_scp.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#pragma once
+
+/* start scp processor */
+void start_scpsys(void);
+
+/* stop scp processor */
+void stop_scpsys(void);
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/mtk_devinfo.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mtk_devinfo.h
new file mode 100644
index 0000000..26afe52
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mtk_devinfo.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2020 MediaTek Inc.
+ *
+ * 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
+ */
+
+#pragma once
+
+#include <devinfo.h>
+
+int det_ca35_freq(void);
+int det_ca72_freq(void);
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/mtk_key.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mtk_key.h
new file mode 100644
index 0000000..d89f5a1
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mtk_key.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#pragma once
+
+bool check_download_key(void);
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/mtk_mrdump.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mtk_mrdump.h
new file mode 100644
index 0000000..8d7c7ca
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mtk_mrdump.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _LK_MTK_MRDUMP_H_
+#define _LK_MTK_MRDUMP_H_
+
+#include <platform/mt2712.h>
+//last 8KB for mcontrol
+#define MRDUMP_CB_ADDR (SRAM_BASE_PHYS + SRAM_BASE_SIZE - MRDUMP_CB_SIZE) //end -8K
+
+#define MRDUMP_OUTPUT_PARTITION "userdata"
+
+#endif /* _LK_MTK_MRDUMP_H_ */
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/mtk_nor.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mtk_nor.h
new file mode 100644
index 0000000..747460a
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mtk_nor.h
@@ -0,0 +1,4 @@
+#pragma once
+
+void nor_init_device(void);
+
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/mtk_serial_key.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mtk_serial_key.h
new file mode 100644
index 0000000..5b05e1f
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mtk_serial_key.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#pragma once
+
+/* serial key */
+#if WITH_KERNEL_VM
+#define SERIAL_KEY_HI    (18446744005260632388UL)
+#define SERIAL_KEY_LO    (18446744005260632384UL)
+#define SERIAL_KEY_2_HI  (18446744005260632396UL)
+#define SERIAL_KEY_2_LO  (18446744005260632392UL)
+#else
+#define SERIAL_KEY_HI    (270557508U)
+#define SERIAL_KEY_LO    (270557504U)
+#define SERIAL_KEY_2_HI  (270557516U)
+#define SERIAL_KEY_2_LO  (270557512U)
+#endif
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/mtk_trng.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mtk_trng.h
new file mode 100644
index 0000000..ce762ce
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mtk_trng.h
@@ -0,0 +1,40 @@
+/* Copyright Statement:
+*
+* This software/firmware and related documentation ("MediaTek Software") are
+* protected under relevant copyright laws. The information contained herein
+* is confidential and proprietary to MediaTek Inc. and/or its licensors.
+* Without the prior written permission of MediaTek inc. and/or its licensors,
+* any reproduction, modification, use or disclosure of MediaTek Software,
+* and information contained herein, in whole or in part, shall be strictly prohibited.
+*
+* MediaTek Inc. (C) 2017. All rights reserved.
+*
+* BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+* THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+* RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+* AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+* NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+* SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+* SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+* THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+* THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+* CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+* SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+* STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+* CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+* AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+* OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+* MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+*
+* The following software/firmware and/or related documentation ("MediaTek Software")
+* have been modified by MediaTek Inc. All revisions are subject to any receiver\'s
+* applicable license agreements with MediaTek Inc.
+*/
+
+#ifndef __MTK_TRNG_H__
+#define __MTK_TRNG_H__
+s32 trng_drv_get_random_data(u8 *buf, u32 len);
+
+#endif  /* !defined __MTK_TRNG_H__ */
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/mtk_wdt.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mtk_wdt.h
new file mode 100644
index 0000000..2dd243a
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/mtk_wdt.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#pragma once
+
+#include <platform/mt_reg_base.h>
+#include <stdbool.h>
+
+#define ENABLE_WDT_MODULE       (1) /* Module switch */
+#define LK_WDT_DISABLE          (1)
+
+#define MTK_WDT_BASE            TOP_RGU_BASE
+
+#define MTK_WDT_MODE			(MTK_WDT_BASE+0x0000)
+#define MTK_WDT_LENGTH			(MTK_WDT_BASE+0x0004)
+#define MTK_WDT_RESTART			(MTK_WDT_BASE+0x0008)
+#define MTK_WDT_STATUS			(MTK_WDT_BASE+0x000C)
+#define MTK_WDT_INTERVAL		(MTK_WDT_BASE+0x0010)
+#define MTK_WDT_SWRST			(MTK_WDT_BASE+0x0014)
+#define MTK_WDT_SWSYSRST		(MTK_WDT_BASE+0x0018)
+#define MTK_WDT_NONRST_REG		(MTK_WDT_BASE+0x0020)
+#define MTK_WDT_NONRST_REG2		(MTK_WDT_BASE+0x0024)
+#define MTK_WDT_REQ_MODE		(MTK_WDT_BASE+0x0030)
+#define MTK_WDT_REQ_IRQ_EN		(MTK_WDT_BASE+0x0034)
+#define MTK_WDT_DRAMC_CTL		(MTK_WDT_BASE+0x0040)
+#define MTK_WDT_DEBUG_2_REG		(MTK_WDT_BASE+0x0508)
+
+/*WDT_MODE*/
+#define MTK_WDT_MODE_KEYMASK        (0xff00)
+#define MTK_WDT_MODE_KEY        (0x22000000)
+#define MTK_WDT_MODE_DDR_RESERVE  (0x0080)
+
+#define MTK_WDT_MODE_DUAL_MODE  (0x0040)
+#define MTK_WDT_MODE_IN_DIS     (0x0020) /* Reserved */
+#define MTK_WDT_MODE_AUTO_RESTART   (0x0010) /* Reserved */
+#define MTK_WDT_MODE_IRQ        (0x0008)
+#define MTK_WDT_MODE_EXTEN      (0x0004)
+#define MTK_WDT_MODE_EXT_POL        (0x0002)
+#define MTK_WDT_MODE_ENABLE     (0x0001)
+
+/*WDT_LENGTH*/
+#define MTK_WDT_LENGTH_TIME_OUT     (0xffe0)
+#define MTK_WDT_LENGTH_KEYMASK      (0x001f)
+#define MTK_WDT_LENGTH_KEY      (0x0008)
+
+/*WDT_RESTART*/
+#define MTK_WDT_RESTART_KEY     (0x1971)
+
+/*WDT_STATUS*/
+#define MTK_WDT_STATUS_HWWDT_RST_WITH_IRQ    (0xA0000000)
+#define MTK_WDT_STATUS_HWWDT_RST    (0x80000000)
+#define MTK_WDT_STATUS_SWWDT_RST    (0x40000000)
+#define MTK_WDT_STATUS_IRQWDT_RST   (0x20000000)
+#define MTK_WDT_STATUS_SECURITY_RST (1<<28)
+#define MTK_WDT_STATUS_DEBUGWDT_RST (0x00080000)
+#define MTK_WDT_STATUS_THERMAL_CTL_RST   (1<<18)
+#define MTK_WDT_STATUS_SPMWDT_RST          (0x0002)
+#define MTK_WDT_STATUS_SPM_THERMAL_RST     (0x0001)
+
+/* Reboot reason */
+#define RE_BOOT_REASON_UNKNOW           (0x00)
+#define RE_BOOT_BY_WDT_HW               (0x01)
+#define RE_BOOT_BY_WDT_SW               (0x02)
+#define RE_BOOT_WITH_INTTERUPT          (0x04)
+#define RE_BOOT_BY_SPM_THERMAL          (0x08)
+#define RE_BOOT_BY_SPM                  (0x10)
+#define RE_BOOT_BY_THERMAL_DIRECT       (0x20)
+#define RE_BOOT_BY_DEBUG                (0x40)
+#define RE_BOOT_BY_SECURITY             (0x80)
+
+#define RE_BOOT_ABNORMAL                (0xF0)
+
+#define WDT_NORMAL_REBOOT               (0x100)
+#define WDT_BY_PASS_PWK_REBOOT          (0x200)
+#define WDT_NOT_WDT_REBOOT              (0x400)
+//MTK_WDT_DEBUG_CTL
+#define MTK_DEBUG_CTL_KEY           (0x59000000)
+#define MTK_RG_DDR_PROTECT_EN       (0x00001)
+#define MTK_RG_MCU_LATH_EN          (0x00002)
+#define MTK_RG_DRAMC_SREF           (0x00100)
+#define MTK_RG_DRAMC_ISO            (0x00200)
+#define MTK_RG_CONF_ISO             (0x00400)
+#define MTK_DDR_RESERVE_RTA         (0x10000)  //sta
+#define MTK_DDR_SREF_STA            (0x20000)  //sta
+
+/* WDT_NONRST_REG2  */
+#define MTK_WDT_NONRST2_BOOT_MASK   (0xF)
+#define MTK_WDT_NONRST2_BOOT_CHARGER    1
+#define MTK_WDT_NONRST2_BOOT_RECOVERY   2
+#define MTK_WDT_NONRST2_BOOT_BOOTLOADER 3
+#define MTK_WDT_NONRST2_BOOT_DM_VERITY  4
+#define MTK_WDT_NONRST2_BOOT_KPOC       5
+#define MTK_WDT_NONRST2_BOOT_DDR_RSVD   6
+#define MTK_WDT_NONRST2_BOOT_META       7
+#define MTK_WDT_NONRST2_BOOT_RPMBPK     8
+#define MTK_WDT_NONRST2_BOOT_FASTBOOTD  9
+#define MTK_WDT_NONRST2_CM4_RESUME      21
+#define MTK_WDT_NONRST2_SHOW_LOGO       22
+
+/*WDT_INTERVAL*/
+#define MTK_WDT_INTERVAL_MASK       (0x0fff)
+
+/*WDT_SWRST*/
+#define MTK_WDT_SWRST_KEY       (0x1209)
+
+/*WDT_SWSYSRST*/
+#define MTK_WDT_SWSYS_RST_PWRAP_SPI_CTL_RST (0x0800)
+#define MTK_WDT_SWSYS_RST_APMIXED_RST   (0x0400)
+#define MTK_WDT_SWSYS_RST_MD_LITE_RST   (0x0200)
+#define MTK_WDT_SWSYS_RST_INFRA_AO_RST  (0x0100)
+#define MTK_WDT_SWSYS_RST_MD_RST    (0x0080)
+#define MTK_WDT_SWSYS_RST_DDRPHY_RST    (0x0040)
+#define MTK_WDT_SWSYS_RST_IMG_RST   (0x0020)
+#define MTK_WDT_SWSYS_RST_VDEC_RST  (0x0010)
+#define MTK_WDT_SWSYS_RST_VENC_RST  (0x0008)
+#define MTK_WDT_SWSYS_RST_MFG_RST   (0x0004)
+#define MTK_WDT_SWSYS_RST_DISP_RST  (0x0002)
+#define MTK_WDT_SWSYS_RST_INFRA_RST (0x0001)
+
+#define MTK_WDT_SWSYS_RST_KEY       (0x88000000)
+
+typedef enum wd_swsys_reset_type {
+    WD_MD_RST,
+} WD_SYS_RST_TYPE;
+
+void set_clr_fastboot_mode(bool flag);
+void set_clr_fastbootd_mode(bool flag);
+void set_clr_recovery_mode(bool flag);
+void clr_cm4_resume_mode(void);
+bool check_fastboot_mode(void);
+bool check_fastbootd_mode(void);
+bool check_recovery_mode(void);
+unsigned int mtk_wdt_check_status(void);
+void mtk_wdt_init(void);
+void mtk_wdt_disable(void);
+void rgu_dram_reserved(bool enable);
+int rgu_is_reserve_ddr_enabled(void);
+
+int rgu_is_dram_slf(void);
+
+void rgu_release_rg_dramc_conf_iso(void);
+
+void rgu_release_rg_dramc_iso(void);
+
+void rgu_release_rg_dramc_sref(void);
+int rgu_is_reserve_ddr_mode_success(void);
+void mtk_arch_reset(char mode);
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/nand/mtk_ecc_hal.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/nand/mtk_ecc_hal.h
new file mode 100644
index 0000000..220a55e
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/nand/mtk_ecc_hal.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2017 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#pragma once
+
+#include <kernel/event.h>
+#include <kernel/mutex.h>
+#include <sys/types.h>
+
+#define     ECC_IDLE_MASK       NAND_BIT(0)
+#define     ECC_IRQ_EN          NAND_BIT(0)
+#define     ECC_OP_ENABLE       (1)
+#define     ECC_OP_DISABLE      (0)
+
+#define     ECC_ENCCON          (0x00)
+#define     ECC_ENCCNFG         (0x04)
+#define     ECC_CNFG_4BIT       (0)
+#define     ECC_CNFG_6BIT       (1)
+#define     ECC_CNFG_8BIT       (2)
+#define     ECC_CNFG_10BIT      (3)
+#define     ECC_CNFG_12BIT      (4)
+#define     ECC_CNFG_14BIT      (5)
+#define     ECC_CNFG_16BIT      (6)
+#define     ECC_CNFG_18BIT      (7)
+#define     ECC_CNFG_20BIT      (8)
+#define     ECC_CNFG_22BIT      (9)
+#define     ECC_CNFG_24BIT      (0xa)
+#define     ECC_CNFG_28BIT      (0xb)
+#define     ECC_CNFG_32BIT      (0xc)
+#define     ECC_CNFG_36BIT      (0xd)
+#define     ECC_CNFG_40BIT      (0xe)
+#define     ECC_CNFG_44BIT      (0xf)
+#define     ECC_CNFG_48BIT      (0x10)
+#define     ECC_CNFG_52BIT      (0x11)
+#define     ECC_CNFG_56BIT      (0x12)
+#define     ECC_CNFG_60BIT      (0x13)
+#define     ECC_CNFG_68BIT      (0x14)
+#define     ECC_CNFG_72BIT      (0x15)
+#define     ECC_CNFG_80BIT      (0x16)
+#define     ECC_MODE_SHIFT      (5)
+#define     ECC_MS_SHIFT        (16)
+#define     ECC_ENCDIADDR       (0x08)
+#define     ECC_ENCIDLE         (0x0c)
+#define     ECC_ENCSTA          (0x7c)
+#define     ENC_IDLE            NAND_BIT(0)
+#define     ECC_ENCIRQ_EN       (0x80)
+#define     ECC_ENCIRQ_STA      (0x84)
+#define     PG_IRQ_SEL          NAND_BIT(1)
+#define     ECC_PIO_DIRDY       (0x90)
+#define     PIO_DI_RDY          (0x01)
+#define     ECC_PIO_DI          (0x94)
+#define     ECC_DECCON          (0x100)
+#define     ECC_DECCNFG         (0x104)
+#define     DEC_EMPTY_EN        NAND_BIT(31)
+#define     DEC_CON_SHIFT       (12)
+#define     ECC_DECDIADDR       (0x108)
+#define     ECC_DECIDLE         (0x10c)
+#define     ECC_DECENUM(x)      (0x114 + (x) * sizeof(u32))
+#define     ERR_MASK            (0x7f)
+#define     ECC_DECDONE         (0x124)
+#define     ECC_DECIRQ_EN       (0x200)
+#define     ECC_DECIRQ_STA      (0x204)
+#define     ECC_DECFSM          (0x208)
+#define     FSM_MASK            (0x3f3fff0f)
+#define     FSM_IDLE            (0x01011101)
+
+#define     ECC_ENCPAR(x)       (0x300 + (x) * sizeof(u32))
+#define     ECC_DECEL(x)        (0x500 + (x) * sizeof(u32))
+#define     DECEL_MASK          (0x3fff)
+#define     ECC_TIMEOUT         (500000)
+
+#define     ECC_IDLE_REG(op)    ((op) == ECC_ENCODE ? ECC_ENCIDLE : ECC_DECIDLE)
+#define     ECC_CTL_REG(op)     ((op) == ECC_ENCODE ? ECC_ENCCON : ECC_DECCON)
+#define     ECC_IRQ_REG(op)     ((op) == ECC_ENCODE ? ECC_ENCIRQ_EN : ECC_DECIRQ_EN)
+#define     ECC_PARITY_BITS     (14)
+#define     MAX_ECC_STRENGTH    (80)
+
+#define     writew(v, a)        (*REG16(a) = (v))
+#define     readw(a)            (*REG16(a))
+
+struct mtk_ecc {
+    mutex_t lock;
+    event_t irq_event;
+    uintptr_t regs;
+    u32 sectors;
+};
+
+enum mtk_ecc_mode {
+    ECC_DMA_MODE = 0,
+    ECC_NFI_MODE = 1,
+    ECC_PIO_MODE = 2
+};
+
+enum mtk_ecc_operation {
+    ECC_ENCODE,
+    ECC_DECODE
+};
+
+enum mtk_ecc_deccon {
+    ECC_DEC_FER = 1,
+    ECC_DEC_LOCATE = 2,
+    ECC_DEC_CORRECT = 3
+};
+
+struct mtk_ecc_stats {
+    u32 corrected;
+    u32 bitflips;
+    u32 failed;
+};
+
+struct mtk_ecc_config {
+    enum mtk_ecc_operation op;
+    enum mtk_ecc_mode mode;
+    enum mtk_ecc_deccon deccon;
+    u32 addr;
+    u32 strength;
+    u32 sectors;
+    u32 len;
+};
+
+int mtk_ecc_encode(struct mtk_ecc *ecc, struct mtk_ecc_config *config,
+                          u8 *data, u32 bytes, int polling);
+int mtk_ecc_enable(struct mtk_ecc *ecc, struct mtk_ecc_config *config, int polling);
+void mtk_ecc_disable(struct mtk_ecc *ecc);
+void mtk_ecc_get_stats(struct mtk_ecc *ecc, struct mtk_ecc_stats *stats, u32 sectors);
+int mtk_ecc_cpu_correct(struct mtk_ecc *ecc, struct mtk_ecc_stats *stats, u8 *data, u32 sector, int polling);
+int mtk_ecc_wait_done(struct mtk_ecc *ecc, enum mtk_ecc_operation op, int polling);
+int mtk_ecc_hw_init(struct mtk_ecc **ext_ecc);
+int mtk_ecc_wait_decode_fsm_idle(struct mtk_ecc *ecc);
+int mtk_ecc_decode(struct mtk_ecc *ecc, struct mtk_ecc_config *config, u8 *data, u32 len, int polling);
+
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/nand/mtk_nand_bbt.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/nand/mtk_nand_bbt.h
new file mode 100644
index 0000000..3fdb9fb
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/nand/mtk_nand_bbt.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2017 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#pragma once
+
+#include <platform/nand/mtk_nand_nal.h>
+
+#define SCAN_BBT_MAXBLOCKS      4
+
+int mtk_nand_isbad_bbt(struct mtk_nand_chip *chip, u32 page);
+int mtk_nand_scan_bbt(struct mtk_nand_chip *chip);
+int mtk_nand_markbad_bbt(struct mtk_nand_chip *chip, u32 page);
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/nand/mtk_nand_common.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/nand/mtk_nand_common.h
new file mode 100644
index 0000000..c009b75
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/nand/mtk_nand_common.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2017 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#pragma once
+
+#include <platform.h>
+#include <trace.h>
+
+#define LOCAL_TRACE 0
+
+#define NAND_BIT(nr)        (1UL << (nr))
+#define NAND_GENMASK(h, l)  (((~0UL) << (l)) & (~0UL >> ((sizeof(unsigned long) * 8) - 1 - (h))))
+#define DIV_ROUND_UP(n,d)   (((n) + (d) - 1) / (d))
+#define clamp(val, lo, hi)  MIN((typeof(val))MAX(val, lo), hi)
+
+#define MTK_TIMEOUT         (500000)
+
+#define KB(x)               ((x) * 1024UL)
+#define MB(x)               (KB(x) * 1024UL)
+
+#define swap(a, b) \
+    do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while(0)
+
+/*
+ * wait until cond gets true or timeout.
+ *
+ * cond : C expression to wait
+ * timeout : usecs
+ *
+ * Returns:
+ * 0 : if cond = false after timeout elapsed.
+ * 1 : if cond = true after timeout elapsed,
+ * or the remain usecs if cond = true before timeout elapsed.
+ */
+#define check_with_timeout(cond, timeout)                      \
+({                                                             \
+    lk_bigtime_t __ret;                                        \
+    if (cond) {                                                \
+        __ret = timeout;                                       \
+    } else {                                                   \
+        lk_bigtime_t __end = current_time_hires() + timeout;   \
+                                                               \
+        for (;;) {                                             \
+            lk_bigtime_t __now = current_time_hires();         \
+                                                               \
+            if (cond) {                                        \
+                __ret = (__end > __now) ? (__end - __now) : 1; \
+                    break;                                     \
+            }                                                  \
+                                                               \
+            if (__end <= __now) {                              \
+                __ret = 0;                                     \
+                break;                                         \
+            }                                                  \
+        }                                                      \
+    }                                                          \
+    __ret;                                                     \
+})
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/nand/mtk_nand_nal.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/nand/mtk_nand_nal.h
new file mode 100644
index 0000000..545e915
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/nand/mtk_nand_nal.h
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2017 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#pragma once
+
+#include <platform/nand/mtk_ecc_hal.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+/* Select the chip by setting nCE to low */
+#define NAND_NCE                    0x01
+/* Select the command latch by setting CLE to high */
+#define NAND_CLE                    0x02
+/* Select the address latch by setting ALE to high */
+#define NAND_ALE                    0x04
+
+#define NAND_CTRL_CLE               (NAND_NCE | NAND_CLE)
+#define NAND_CTRL_ALE               (NAND_NCE | NAND_ALE)
+#define NAND_CTRL_CHANGE            0x80
+
+/*
+ * Standard NAND flash commands
+ */
+#define NAND_CMD_READ0              0
+#define NAND_CMD_READ1              1
+#define NAND_CMD_RNDOUT             5
+#define NAND_CMD_PAGEPROG           0x10
+#define NAND_CMD_READOOB            0x50
+#define NAND_CMD_ERASE1             0x60
+#define NAND_CMD_STATUS             0x70
+#define NAND_CMD_SEQIN              0x80
+#define NAND_CMD_RNDIN              0x85
+#define NAND_CMD_READID             0x90
+#define NAND_CMD_ERASE2             0xd0
+#define NAND_CMD_PARAM              0xec
+#define NAND_CMD_GET_FEATURES       0xee
+#define NAND_CMD_SET_FEATURES       0xef
+#define NAND_CMD_RESET              0xff
+#define NAND_CMD_LOCK               0x2a
+#define NAND_CMD_UNLOCK1            0x23
+#define NAND_CMD_UNLOCK2            0x24
+
+/* Extended commands for large page devices */
+#define NAND_CMD_READSTART          0x30
+#define NAND_CMD_READCACHESEQ       0x31
+#define NAND_CMD_READCACHELAST      0x3f
+#define NAND_CMD_RNDOUTSTART        0xE0
+#define NAND_CMD_CACHEDPROG         0x15
+#define NAND_CMD_NONE               -1
+
+/* Status bits */
+#define NAND_STATUS_FAIL            0x01
+#define NAND_STATUS_FAIL_N1         0x02
+#define NAND_STATUS_TRUE_READY      0x20
+#define NAND_STATUS_READY           0x40
+#define NAND_STATUS_WP              0x80
+
+/* Search good / bad pattern on the first and the second page */
+#define NAND_BBT_SCAN2NDPAGE        0x00008000
+/* Search good / bad pattern on the last page of the eraseblock */
+#define NAND_BBT_SCANLASTPAGE       0x00010000
+
+/* Chip has cache read function */
+#define NAND_CACHEREAD              0x00000004
+/* Chip has cache program function */
+#define NAND_CACHEPRG               0x00000008
+
+/*
+ * Some MLC NANDs need data scrambling to limit bitflips caused by repeated
+ * patterns.
+ */
+#define NAND_NEED_SCRAMBLING        0x00002000
+
+#define NAND_HAS_CACHEPROG(chip)    ((chip->options & NAND_CACHEPRG))
+#define NAND_HAS_CACHEREAD(chip)    ((chip->options & NAND_CACHEREAD))
+
+/* Max NAND ID length */
+#define NAND_MAX_ID_LEN             8
+
+struct mtk_nand_flash_dev {
+    const char *name;
+    u8 id[NAND_MAX_ID_LEN];
+    u8 id_len;
+
+    /* unit: KByte */
+    u32 chipsize;
+    u32 erasesize;
+    u32 pagesize;
+    u16 oobsize;
+    u32 fdmeccsize;
+    u8 bits_per_cell;
+
+    /* customized setting if need */
+    u32 acctiming;
+    u32 ecc_size;
+    u32 ecc_strength;
+    u32 bbt_options;
+    u32 options;
+};
+
+enum {
+    NAND_OPS_RAW_DMA_POLL = 0,
+    NAND_OPS_RAW_DMA_IRQ,
+    NAND_OPS_RAW_PIO_POLL,
+    NAND_OPS_RAW_PIO_IRQ,
+    NAND_OPS_ECC_DMA_POLL,
+    NAND_OPS_ECC_DMA_IRQ,
+    NAND_OPS_ECC_PIO_POLL,
+    NAND_OPS_ECC_PIO_IRQ,
+    NAND_OPS_ERASE_POLL,
+    NAND_OPS_ERASE_IRQ,
+};
+
+enum mtk_randomizer_operation {RAND_ENCODE, RAND_DECODE};
+
+struct mtk_nand_ops {
+    u32 mode;
+    u64 offset;
+    u64 len;
+    const u8 *writebuf;
+    u8 *readbuf;
+    /* ecc protected oob data */
+    u8 *oobeccbuf;
+    u32 oobeccoffs;
+    u32 oobecclen;
+    /* ecc unprotected oob data */
+    u8 *oobrawbuf;
+    u32 oobrawoffs;
+    u32 oobrawlen;
+    /* ecc parity data */
+    u8 *oobparitybuf;
+    u32 oobparityoffs;
+    u32 oobparitylen;
+};
+
+struct mtk_nand_chip {
+    u8 (*read_byte)(struct mtk_nand_chip *nand);
+    void (*write_byte)(struct mtk_nand_chip *nand, u8 byte);
+    void (*write_buf)(struct mtk_nand_chip *nand, const u8 *buf, int len);
+    void (*read_buf)(struct mtk_nand_chip *nand, u8 *buf, int len);
+    void (*select_chip)(struct mtk_nand_chip *nand, int chip);
+    void (*cmd_ctrl)(struct mtk_nand_chip *nand, int dat, unsigned int ctrl);
+    int (*dev_ready)(struct mtk_nand_chip *nand);
+    int (*wait_busy_irq)(struct mtk_nand_chip *nand);
+    void (*cmdfunc)(struct mtk_nand_chip *nand, unsigned command, int column,
+                    int page_addr);
+    int(*waitfunc)(struct mtk_nand_chip *this, int polling);
+
+    int (*block_bad)(struct mtk_nand_chip *nand, u64 ofs);
+    int (*block_markbad)(struct mtk_nand_chip *nand, u64 ofs);
+
+    int (*write_page_ecc_dma_polling)(struct mtk_nand_chip *chip, const u8 *buf,
+                                      int page);
+    int (*write_page_ecc_dma_irq)(struct mtk_nand_chip *chip, const u8 *buf,
+                                  int page);
+    int (*write_page_ecc_pio_polling)(struct mtk_nand_chip *chip, const u8 *buf,
+                                      int page);
+    int (*write_page_ecc_pio_irq)(struct mtk_nand_chip *chip, const u8 *buf,
+                                  int page);
+    int (*write_page_raw_dma_polling)(struct mtk_nand_chip *chip, const u8 *buf,
+                                      int page);
+    int (*write_page_raw_dma_irq)(struct mtk_nand_chip *chip, const u8 *buf,
+                                  int page);
+    int (*write_page_raw_pio_polling)(struct mtk_nand_chip *chip, const u8 *buf,
+                                      int page);
+    int (*write_page_raw_pio_irq)(struct mtk_nand_chip *chip, const u8 *buf,
+                                  int page);
+    int (*write_subpage_ecc_dma_polling)(struct mtk_nand_chip *chip, u32 offset,
+                                         u32 data_len, const u8 *buf, int page);
+    int (*write_subpage_ecc_dma_irq)(struct mtk_nand_chip *chip, u32 offset,
+                                     u32 data_len, const u8 *buf, int page);
+    int (*write_subpage_ecc_pio_polling)(struct mtk_nand_chip *chip, u32 offset,
+                                         u32 data_len, const u8 *buf, int page);
+    int (*write_subpage_ecc_pio_irq)(struct mtk_nand_chip *chip, u32 offset,
+                                     u32 data_len, const u8 *buf, int page);
+
+    int (*read_subpage_ecc_dma_polling)(struct mtk_nand_chip *chip, u32 off,
+                                        u32 len, u8 *p, int pg);
+    int (*read_subpage_ecc_dma_irq)(struct mtk_nand_chip *chip, u32 off,
+                                    u32 len, u8 *p, int pg);
+    int (*read_subpage_ecc_pio_polling)(struct mtk_nand_chip *chip, u32 off,
+                                        u32 len, u8 *p, int pg);
+    int (*read_subpage_ecc_pio_irq)(struct mtk_nand_chip *chip, u32 off,
+                                    u32 len, u8 *p, int pg);
+    int (*read_page_ecc_dma_polling)(struct mtk_nand_chip *chip, u8 *p, int pg);
+    int (*read_page_ecc_dma_irq)(struct mtk_nand_chip *chip, u8 *p, int pg);
+    int (*read_page_ecc_pio_polling)(struct mtk_nand_chip *chip, u8 *p, int pg);
+    int (*read_page_ecc_pio_irq)(struct mtk_nand_chip *chip, u8 *p, int pg);
+    int (*read_page_raw_dma_polling)(struct mtk_nand_chip *chip, u8 *buf, int page);
+    int (*read_page_raw_dma_irq)(struct mtk_nand_chip *chip, u8 *buf, int page);
+    int (*read_page_raw_pio_polling)(struct mtk_nand_chip *chip, u8 *buf, int page);
+    int (*read_page_raw_pio_irq)(struct mtk_nand_chip *chip, u8 *buf, int page);
+    void (*enable_randomizer)(struct mtk_nand_chip *chip, int page,
+                              enum mtk_randomizer_operation rand, int repage);
+    void (*disable_randomizer)(struct mtk_nand_chip *chip);
+    int (*fill_oob_ecc)(struct mtk_nand_chip *chip, u8* buf, u32 offset, u32 len);
+    int (*fill_oob_raw)(struct mtk_nand_chip *chip, u8* buf, u32 offset, u32 len);
+    int (*fill_oob_parity)(struct mtk_nand_chip *chip, u8* buf, u32 offset, u32 len);
+    int (*transfer_oob_ecc)(struct mtk_nand_chip *chip, u8* buf, u32 offset, u32 len);
+    int (*transfer_oob_raw)(struct mtk_nand_chip *chip, u8* buf, u32 offset, u32 len);
+    int (*transfer_oob_parity)(struct mtk_nand_chip *chip, u8* buf, u32 offset, u32 len);
+
+    /* nand device information */
+    u64 totalsize;
+    /* unit: Byte */
+    u64 chipsize;
+    u32 pagesize;
+    u32 oobsize;
+    u32 blocksize;
+    u32 ecc_size;
+    u32 ecc_strength;
+    u32 ecc_steps;
+    u32 subpagesize;
+    u32 fdm_ecc_size;
+    u32 oob_free_ecc_size;
+    u32 oob_free_raw_size;
+    u8 bits_per_cell;
+    u32 page_per_chip;
+    u32 page_per_block;
+    int chip_delay;
+    u32 options;
+    u8 numchips;
+    int activechip;
+
+    u8 *databuf;
+    u8 *oob_poi;
+
+    u8 *bbt;
+    u32 bbt_options;
+    int bbt_block;
+    int badblockpos;
+    int badblockbits;
+
+    struct mtk_ecc_stats stats;
+
+    void *priv;
+};
+
+static inline void *nand_get_controller_data(struct mtk_nand_chip *chip)
+{
+    return chip->priv;
+}
+
+static inline void nand_set_controller_data(struct mtk_nand_chip *chip, void *priv)
+{
+    chip->priv = priv;
+}
+
+static inline bool nand_is_slc(struct mtk_nand_chip *chip)
+{
+    return chip->bits_per_cell == 1;
+}
+
+extern struct mtk_nand_flash_dev nand_flash_devs[];
+int mtk_nand_erase(struct mtk_nand_chip *chip, struct mtk_nand_ops *ops);
+int mtk_nand_write(struct mtk_nand_chip *chip, struct mtk_nand_ops *ops);
+int mtk_nand_read(struct mtk_nand_chip *chip, struct mtk_nand_ops *ops);
+int mtk_nand_block_isbad(struct mtk_nand_chip *chip, u32 page);
+int mtk_nand_block_markbad(struct mtk_nand_chip *chip, u32 page);
+int mtk_nand_init(void);
+int mtk_nand_scan(struct mtk_nand_chip *chip, int maxchips);
+int mtk_nand_scan_tail(struct mtk_nand_chip *chip);
+int nand_reset(struct mtk_nand_chip *chip, int chipnr);
+struct mtk_nand_chip *mtk_get_nand_chip(void);
+int mtk_nand_block_checkbad(struct mtk_nand_chip *chip, u32 page);
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/nand/mtk_nfi_hal.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/nand/mtk_nfi_hal.h
new file mode 100644
index 0000000..6f00249
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/nand/mtk_nfi_hal.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2017 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#pragma once
+
+#include <platform/nand/mtk_nand_nal.h>
+#include <kernel/mutex.h>
+#include <kernel/event.h>
+
+#define     NFI_CNFG                (0x00)
+#define     CNFG_AHB                NAND_BIT(0)
+#define     CNFG_READ_EN            NAND_BIT(1)
+#define     CNFG_DMA_BURST_EN       NAND_BIT(2)
+#define     CNFG_RESEED_SEC_EN      NAND_BIT(4)
+#define     CNFG_RAND_SEL           NAND_BIT(5)
+#define     CNFG_RAND_MASK          (3 << 4)
+#define     CNFG_BYTE_RW            NAND_BIT(6)
+#define     CNFG_HW_ECC_EN          NAND_BIT(8)
+#define     CNFG_AUTO_FMT_EN        NAND_BIT(9)
+#define     CNFG_OP_CUST            (6 << 12)
+#define     NFI_PAGEFMT             (0x04)
+#define     PAGEFMT_FDM_ECC_SHIFT   (12)
+#define     PAGEFMT_FDM_SHIFT       (8)
+#define     PAGEFMT_SPARE_16        (0)
+#define     PAGEFMT_SPARE_26        (1)
+#define     PAGEFMT_SPARE_27        (2)
+#define     PAGEFMT_SPARE_28        (3)
+#define     PAGEFMT_SPARE_32        (4)
+#define     PAGEFMT_SPARE_36        (5)
+#define     PAGEFMT_SPARE_40        (6)
+#define     PAGEFMT_SPARE_44        (7)
+#define     PAGEFMT_SPARE_48        (8)
+#define     PAGEFMT_SPARE_49        (9)
+#define     PAGEFMT_SPARE_50        (0xa)
+#define     PAGEFMT_SPARE_51        (0xb)
+#define     PAGEFMT_SPARE_52        (0xc)
+#define     PAGEFMT_SPARE_62        (0xd)
+#define     PAGEFMT_SPARE_61        (0xe)
+#define     PAGEFMT_SPARE_63        (0xf)
+#define     PAGEFMT_SPARE_64        (0x10)
+#define     PAGEFMT_SPARE_67        (0x11)
+#define     PAGEFMT_SPARE_74        (0x12)
+#define     PAGEFMT_SPARE_SHIFT     (16)
+#define     PAGEFMT_SEC_SEL_512     NAND_BIT(2)
+#define     PAGEFMT_512_2K          (0)
+#define     PAGEFMT_2K_4K           (1)
+#define     PAGEFMT_4K_8K           (2)
+#define     PAGEFMT_8K_16K          (3)
+#define     NFI_CON                 (0x08)
+#define     CON_FIFO_FLUSH          NAND_BIT(0)
+#define     CON_NFI_RST             NAND_BIT(1)
+#define     CON_BRD                 NAND_BIT(8)  /* burst  read */
+#define     CON_BWR                 NAND_BIT(9) /* burst  write */
+#define     CON_SEC_SHIFT           (12)
+#define     NFI_ACCCON              (0x0c)
+#define     NFI_INTR_EN             (0x10)
+#define     INTR_BUSY_RETURN_EN     NAND_BIT(4)
+#define     INTR_AHB_DONE_EN        NAND_BIT(6)
+#define     NFI_INTR_STA            (0x14)
+#define     NFI_CMD                 (0x20)
+#define     NFI_ADDRNOB             (0x30)
+#define     NFI_COLADDR             (0x34)
+#define     NFI_ROWADDR             (0x38)
+#define     NFI_STRDATA             (0x40)
+#define     STAR_EN                 (1)
+#define     STAR_DE                 (0)
+#define     NFI_CNRNB               (0x44)
+#define     NFI_DATAW               (0x50)
+#define     NFI_DATAR               (0x54)
+#define     NFI_PIO_DIRDY           (0x58)
+#define     PIO_DI_RDY              (0x01)
+#define     NFI_STA                 (0x60)
+#define     STA_CMD                 NAND_BIT(0)
+#define     STA_ADDR                NAND_BIT(1)
+#define     STA_BUSY                NAND_BIT(8)
+#define     STA_EMP_PAGE            NAND_BIT(12)
+#define     NFI_FSM_CUSTDATA        (0xe << 16)
+#define     NFI_FSM_MASK            (0xf << 16)
+#define     NFI_ADDRCNTR            (0x70)
+#define     CNTR_MASK               NAND_GENMASK(16, 12)
+#define     ADDRCNTR_SEC_SHIFT      (12)
+#define     ADDRCNTR_SEC(val)       (((val) & CNTR_MASK) >> ADDRCNTR_SEC_SHIFT)
+#define     NFI_STRADDR             (0x80)
+#define     NFI_BYTELEN             (0x84)
+#define     NFI_CSEL                (0x90)
+#define     NFI_FDML(x)             (0xa0 + (x) * sizeof(u32) * 2)
+#define     NFI_FDMM(x)             (0xa4 + (x) * sizeof(u32) * 2)
+#define     NFI_FDM_MAX_SIZE        (8)
+#define     NFI_FDM_MIN_SIZE        (1)
+#define     NFI_MASTER_STA          (0x224)
+#define     MASTER_STA_MASK         (0x0FFF)
+#define     MASTER_BUS_BUSY         (0x3)
+#define     NFI_RANDOM_CNFG         (0x238)
+#define     RAN_ENCODE_EN           NAND_BIT(0)
+#define     ENCODE_SEED_SHIFT       (1)
+#define     RAN_DECODE_EN           NAND_BIT(16)
+#define     DECODE_SEED_SHIFT       (17)
+#define     RAN_SEED_MASK           (0x7fff)
+#define     RAND_SEED_SHIFT(op)     \
+    ((op) == RAND_ENCODE ? ENCODE_SEED_SHIFT : DECODE_SEED_SHIFT)
+#define     RAND_EN(op)             \
+    ((op) == RAND_ENCODE ? RAN_ENCODE_EN : RAN_DECODE_EN)
+#define     NFI_EMPTY_THRESH        (0x23c)
+
+#define     MTK_RESET_TIMEOUT       (1000000)
+#define     MTK_MAX_SECTOR          (16)
+#define     MTK_NAND_MAX_NSELS      (2)
+
+struct mtk_nfc_bad_mark_ctl {
+    void (*bm_swap)(struct mtk_nand_chip *chip, u8 *buf, int raw);
+    u32 sec;
+    u32 pos;
+};
+
+/*
+ * FDM: region used to store free OOB data
+ */
+struct mtk_nfc_fdm {
+    u32 reg_size;
+    u32 ecc_size;
+};
+
+struct mtk_nfc {
+    mutex_t lock;
+    event_t irq_event;
+    struct mtk_ecc_config ecc_cfg;
+    struct mtk_ecc *ecc;
+    uintptr_t regs;
+    u8 *buffer;
+};
+
+struct mtk_nfc_nand_chip {
+    struct mtk_nand_chip chip;
+    struct mtk_nfc_bad_mark_ctl bad_mark;
+    struct mtk_nfc_fdm fdm;
+    u32 spare_per_sector;
+    u32 acctiming;
+};
+
+int mtk_nfc_nand_chip_init(struct mtk_nand_chip **ext_nand);
+
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/nand/nand.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/nand/nand.h
new file mode 100644
index 0000000..adbaf69
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/nand/nand.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2017 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#pragma once
+
+int nand_init_device(void);
+void nand_dump_device_info(void);
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/platform_blx.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/platform_blx.h
new file mode 100644
index 0000000..a2cd3be
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/platform_blx.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#pragma once
+#include <compiler.h>
+#include <debug.h>
+
+typedef enum {
+    BR_POWER_KEY = 0,
+    BR_USB,
+    BR_RTC,
+    BR_WDT,
+    BR_WDT_BY_PASS_PWK,
+    BR_TOOL_BY_PASS_PWK,
+    BR_2SEC_REBOOT,
+    BR_UNKNOWN,
+    BR_KERNEL_PANIC,
+    BR_WDT_SW,
+    BR_WST_HW,
+} boot_reason_t;
+
+void platform_memory_init(void);
+void platform_early_init_blx(void);
+boot_reason_t platform_boot_status(void);
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/pll.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/pll.h
new file mode 100644
index 0000000..00b799c
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/pll.h
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once
+
+#include <platform/mt_reg_base.h>
+
+/* APMIXEDSYS Register */
+#define AP_PLL_CON0             (APMIXED_BASE + 0x000)
+#define AP_PLL_CON2             (APMIXED_BASE + 0x008)
+#define AP_PLL_CON3             (APMIXED_BASE + 0x00C)
+#define AP_PLL_CON4             (APMIXED_BASE + 0x010)
+#define AP_PLL_CON5             (APMIXED_BASE + 0x014)
+#define AP_PLL_CON6             (APMIXED_BASE + 0x018)
+#define AP_PLL_CON7             (APMIXED_BASE + 0x01C)
+#define CLKSQ_STB_CON0          (APMIXED_BASE + 0x020)
+#define PLL_PWR_CON0            (APMIXED_BASE + 0x024)
+#define PLL_PWR_CON1            (APMIXED_BASE + 0x028)
+#define PLL_ISO_CON0            (APMIXED_BASE + 0x02C)
+#define PLL_ISO_CON1            (APMIXED_BASE + 0x030)
+#define PLL_STB_CON0            (APMIXED_BASE + 0x034)
+#define DIV_STB_CON0            (APMIXED_BASE + 0x038)
+#define PLL_CHG_CON0            (APMIXED_BASE + 0x03C)
+#define PLL_TEST_CON0           (APMIXED_BASE + 0x040)
+#define PLL_TEST_CON1           (APMIXED_BASE + 0x044)
+#define ARMCA35PLL_CON0         (APMIXED_BASE + 0x100)
+#define ARMCA35PLL_CON1         (APMIXED_BASE + 0x104)
+#define ARMCA35PLL_CON2         (APMIXED_BASE + 0x108)
+#define ARMCA35PLL_PWR_CON0     (APMIXED_BASE + 0x110)
+#define ARMCA72PLL_CON0         (APMIXED_BASE + 0x210)
+#define ARMCA72PLL_CON1         (APMIXED_BASE + 0x214)
+#define ARMCA72PLL_CON2         (APMIXED_BASE + 0x218)
+#define ARMCA72PLL_PWR_CON0     (APMIXED_BASE + 0x220)
+#define MAINPLL_CON0            (APMIXED_BASE + 0x230)
+#define MAINPLL_CON1            (APMIXED_BASE + 0x234)
+#define MAINPLL_CON2            (APMIXED_BASE + 0x238)
+#define MAINPLL_PWR_CON0        (APMIXED_BASE + 0x23C)
+#define UNIVPLL_CON0            (APMIXED_BASE + 0x240)
+#define UNIVPLL_CON1            (APMIXED_BASE + 0x244)
+#define UNIVPLL_CON2            (APMIXED_BASE + 0x248)
+#define UNIVPLL_PWR_CON0        (APMIXED_BASE + 0x24C)
+#define MMPLL_CON0              (APMIXED_BASE + 0x250)
+#define MMPLL_CON1              (APMIXED_BASE + 0x254)
+#define MMPLL_CON2              (APMIXED_BASE + 0x258)
+#define MMPLL_PWR_CON0          (APMIXED_BASE + 0x260)
+#define MSDCPLL_CON0            (APMIXED_BASE + 0x270)
+#define MSDCPLL_CON1            (APMIXED_BASE + 0x274)
+#define MSDCPLL_CON2            (APMIXED_BASE + 0x278)
+#define MSDCPLL_PWR_CON0        (APMIXED_BASE + 0x27C)
+#define VENCPLL_CON0            (APMIXED_BASE + 0x280)
+#define VENCPLL_CON1            (APMIXED_BASE + 0x284)
+#define VENCPLL_CON2            (APMIXED_BASE + 0x288)
+#define VENCPLL_PWR_CON0        (APMIXED_BASE + 0x28C)
+#define TVDPLL_CON0             (APMIXED_BASE + 0x290)
+#define TVDPLL_CON1             (APMIXED_BASE + 0x294)
+#define TVDPLL_CON2             (APMIXED_BASE + 0x298)
+#define TVDPLL_PWR_CON0         (APMIXED_BASE + 0x29C)
+#define ETHERPLL_CON0           (APMIXED_BASE + 0x300)
+#define ETHERPLL_CON1           (APMIXED_BASE + 0x304)
+#define ETHERPLL_CON2           (APMIXED_BASE + 0x308)
+#define ETHERPLL_PWR_CON0       (APMIXED_BASE + 0x30C)
+#define CVBSPLL_CON0            (APMIXED_BASE + 0x310)
+#define CVBSPLL_CON1            (APMIXED_BASE + 0x314)
+#define CVBSREFPLL_CON0         (APMIXED_BASE + 0x318)
+#define CVBSREFPLL_CON1         (APMIXED_BASE + 0x31C)
+#define VCODECPLL_CON0          (APMIXED_BASE + 0x320)
+#define VCODECPLL_CON1          (APMIXED_BASE + 0x324)
+#define VCODECPLL_CON2          (APMIXED_BASE + 0x328)
+#define VCODECPLL_PWR_CON0      (APMIXED_BASE + 0x32C)
+#define APLL1_CON0              (APMIXED_BASE + 0x330)
+#define APLL1_CON1              (APMIXED_BASE + 0x334)
+#define APLL1_CON2              (APMIXED_BASE + 0x338)
+#define APLL1_CON3              (APMIXED_BASE + 0x33C)
+#define APLL1_PWR_CON0          (APMIXED_BASE + 0x340)
+#define APLL2_CON0              (APMIXED_BASE + 0x350)
+#define APLL2_CON1              (APMIXED_BASE + 0x354)
+#define APLL2_CON2              (APMIXED_BASE + 0x358)
+#define APLL2_CON3              (APMIXED_BASE + 0x35C)
+#define APLL2_PWR_CON0          (APMIXED_BASE + 0x360)
+#define LVDSPLL_CON0            (APMIXED_BASE + 0x370)
+#define LVDSPLL_CON1            (APMIXED_BASE + 0x374)
+#define LVDSPLL_CON2            (APMIXED_BASE + 0x378)
+#define LVDSPLL_PWR_CON0        (APMIXED_BASE + 0x37C)
+#define LVDSPLL_SSC_CON0        (APMIXED_BASE + 0x380)
+#define LVDSPLL_SSC_CON1        (APMIXED_BASE + 0x384)
+#define LVDSPLL_SSC_CON2        (APMIXED_BASE + 0x388)
+#define LVDSPLL2_CON0           (APMIXED_BASE + 0x390)
+#define LVDSPLL2_CON1           (APMIXED_BASE + 0x394)
+#define LVDSPLL2_CON2           (APMIXED_BASE + 0x398)
+#define LVDSPLL2_PWR_CON0       (APMIXED_BASE + 0x39C)
+#define LVDSPLL2_SSC_CON0       (APMIXED_BASE + 0x400)
+#define LVDSPLL2_SSC_CON1       (APMIXED_BASE + 0x404)
+#define LVDSPLL2_SSC_CON2       (APMIXED_BASE + 0x408)
+#define MSDCPLL2_CON0           (APMIXED_BASE + 0x410)
+#define MSDCPLL2_CON1           (APMIXED_BASE + 0x414)
+#define MSDCPLL2_CON2           (APMIXED_BASE + 0x418)
+#define MSDCPLL2_PWR_CON0       (APMIXED_BASE + 0x41C)
+#define AP_AUXADC_CON0          (APMIXED_BASE + 0x420)
+#define AP_AUXADC_CON1          (APMIXED_BASE + 0x424)
+#define TS_CON0                 (APMIXED_BASE + 0x600)
+#define TS_CON1                 (APMIXED_BASE + 0x604)
+#define AP_ABIST_MON_CON0       (APMIXED_BASE + 0x800)
+#define AP_ABIST_MON_CON1       (APMIXED_BASE + 0x804)
+#define AP_ABIST_MON_CON2       (APMIXED_BASE + 0x808)
+#define AP_ABIST_MON_CON3       (APMIXED_BASE + 0x80C)
+#define OCCSCAN_CON0            (APMIXED_BASE + 0x810)
+#define CLKDIV_CON0             (APMIXED_BASE + 0x814)
+#define OCCSCAN_CON1            (APMIXED_BASE + 0x818)
+#define RSV_RW0_CON0            (APMIXED_BASE + 0x900)
+#define RSV_RW1_CON0            (APMIXED_BASE + 0x904)
+#define RSV_RO_CON0             (APMIXED_BASE + 0x908)
+#define AADC_CON0               (APMIXED_BASE + 0x910)
+#define AADC_CON1               (APMIXED_BASE + 0x914)
+#define AADC_CON2               (APMIXED_BASE + 0x918)
+#define AADC_CON3               (APMIXED_BASE + 0x91C)
+#define AADC_CON4               (APMIXED_BASE + 0x920)
+#define CVBS_CON0               (APMIXED_BASE + 0x930)
+#define CVBS_CON1               (APMIXED_BASE + 0x934)
+#define CVBS_CON2               (APMIXED_BASE + 0x938)
+#define CVBS_CON3               (APMIXED_BASE + 0x93C)
+#define CVBS_CON4               (APMIXED_BASE + 0x940)
+
+/* TOPCKGEN Register */
+#define CLK_MODE                (CKSYS_BASE + 0x000)
+#define DCM_CFG                 (CKSYS_BASE + 0x004)
+#define TST_SEL_0               (CKSYS_BASE + 0x020)
+#define TST_SEL_1               (CKSYS_BASE + 0x024)
+#define TST_SEL_2               (CKSYS_BASE + 0x028)
+#define TST_SEL_3               (CKSYS_BASE + 0x02C)
+#define TST_SEL_4               (CKSYS_BASE + 0x030)
+#define TST_SEL_5               (CKSYS_BASE + 0x034)
+#define CLK_CFG_0               (CKSYS_BASE + 0x040)
+#define CLK_CFG_0_SET           (CKSYS_BASE + 0x044)
+#define CLK_CFG_0_CLR           (CKSYS_BASE + 0x048)
+#define CLK_CFG_1               (CKSYS_BASE + 0x050)
+#define CLK_CFG_1_SET           (CKSYS_BASE + 0x054)
+#define CLK_CFG_1_CLR           (CKSYS_BASE + 0x058)
+#define CLK_CFG_2               (CKSYS_BASE + 0x060)
+#define CLK_CFG_2_SET           (CKSYS_BASE + 0x064)
+#define CLK_CFG_2_CLR           (CKSYS_BASE + 0x068)
+#define CLK_CFG_3               (CKSYS_BASE + 0x070)
+#define CLK_CFG_3_SET           (CKSYS_BASE + 0x074)
+#define CLK_CFG_3_CLR           (CKSYS_BASE + 0x078)
+#define CLK_CFG_4               (CKSYS_BASE + 0x080)
+#define CLK_CFG_4_SET           (CKSYS_BASE + 0x084)
+#define CLK_CFG_4_CLR           (CKSYS_BASE + 0x088)
+#define CLK_CFG_5               (CKSYS_BASE + 0x090)
+#define CLK_CFG_5_SET           (CKSYS_BASE + 0x094)
+#define CLK_CFG_5_CLR           (CKSYS_BASE + 0x098)
+#define CLK_CFG_6               (CKSYS_BASE + 0x0A0)
+#define CLK_CFG_6_SET           (CKSYS_BASE + 0x0A4)
+#define CLK_CFG_6_CLR           (CKSYS_BASE + 0x0A8)
+#define CLK_CFG_7               (CKSYS_BASE + 0x0B0)
+#define CLK_CFG_7_SET           (CKSYS_BASE + 0x0B4)
+#define CLK_CFG_7_CLR           (CKSYS_BASE + 0x0B8)
+#define CLK_CFG_8               (CKSYS_BASE + 0x0C0)
+#define CLK_CFG_8_SET           (CKSYS_BASE + 0x0C4)
+#define CLK_CFG_8_CLR           (CKSYS_BASE + 0x0C8)
+#define CLK_CFG_9               (CKSYS_BASE + 0x0D0)
+#define CLK_CFG_9_SET           (CKSYS_BASE + 0x0D4)
+#define CLK_CFG_9_CLR           (CKSYS_BASE + 0x0D8)
+#define CLK_CFG_M0              (CKSYS_BASE + 0x100)
+#define CLK_CFG_M1              (CKSYS_BASE + 0x104)
+#define CLK_CFG_M2              (CKSYS_BASE + 0x108)
+#define CLK_CFG_M3              (CKSYS_BASE + 0x10C)
+#define CLK_AUDDIV_0            (CKSYS_BASE + 0x120)
+#define CLK_AUDDIV_1            (CKSYS_BASE + 0x124)
+#define CLK_AUDDIV_2            (CKSYS_BASE + 0x128)
+#define CLK_AUDDIV_3            (CKSYS_BASE + 0x12C)
+#define CLK_AUDDIV_4            (CKSYS_BASE + 0x134)
+#define CLK_SCP_CFG_0           (CKSYS_BASE + 0x200)
+#define CLK_SCP_CFG_1           (CKSYS_BASE + 0x204)
+#define CLK_MISC_CFG_0          (CKSYS_BASE + 0x210)
+#define CLK_MISC_CFG_1          (CKSYS_BASE + 0x214)
+#define CLK_MISC_CFG_2          (CKSYS_BASE + 0x218)
+#define CLK_APB_MON             (CKSYS_BASE + 0x21C)
+#define CLK26CALI_0             (CKSYS_BASE + 0x220)
+#define CLK26CALI_1             (CKSYS_BASE + 0x224)
+#define CLK26CALI_2             (CKSYS_BASE + 0x228)
+#define CKSTA_REG               (CKSYS_BASE + 0x22C)
+#define TEST_MODE_CFG           (CKSYS_BASE + 0x230)
+#define MBIST_CFG_0             (CKSYS_BASE + 0x308)
+#define MBIST_CFG_1             (CKSYS_BASE + 0x30C)
+#define MBIST_CFG_2             (CKSYS_BASE + 0x310)
+#define MBIST_CFG_3             (CKSYS_BASE + 0x314)
+#define CLK_MSDC_DELAY_SEL_0    (CKSYS_BASE + 0x400)
+#define CLK_MSDC_DELAY_SEL_1    (CKSYS_BASE + 0x404)
+#define CLK_MSDC_DELAY_SEL_2    (CKSYS_BASE + 0x408)
+#define CLK_MSDC_DELAY_SEL_3    (CKSYS_BASE + 0x40C)
+#define CLK_MSDC_DELAY_SEL_4    (CKSYS_BASE + 0x410)
+#define TVD_ANAIF_CFG           (CKSYS_BASE + 0x420)
+#define CLK_CG_EN_CFG           (CKSYS_BASE + 0x424)
+#define ECC_RESET_CFG_1         (CKSYS_BASE + 0x428)
+#define ECC_RESET_CFG_2         (CKSYS_BASE + 0x42C)
+#define NORM_STRAP_CFG          (CKSYS_BASE + 0x430)
+#define CKGEN_BACKUP            (CKSYS_BASE + 0x434)
+#define CLK_CFG_10              (CKSYS_BASE + 0x500)
+#define CLK_CFG_10_SET          (CKSYS_BASE + 0x504)
+#define CLK_CFG_10_CLR          (CKSYS_BASE + 0x508)
+#define CLK_CFG_11              (CKSYS_BASE + 0x510)
+#define CLK_CFG_11_SET          (CKSYS_BASE + 0x514)
+#define CLK_CFG_11_CLR          (CKSYS_BASE + 0x518)
+#define CLK_CFG_12              (CKSYS_BASE + 0x520)
+#define CLK_CFG_12_SET          (CKSYS_BASE + 0x524)
+#define CLK_CFG_12_CLR          (CKSYS_BASE + 0x528)
+#define CLK_CFG_13              (CKSYS_BASE + 0x530)
+#define CLK_CFG_13_SET          (CKSYS_BASE + 0x534)
+#define CLK_CFG_13_CLR          (CKSYS_BASE + 0x538)
+#define CLK_CFG_14              (CKSYS_BASE + 0x540)
+#define CLK_CFG_14_SET          (CKSYS_BASE + 0x544)
+#define CLK_CFG_14_CLR          (CKSYS_BASE + 0x548)
+#define CLK_CFG_15              (CKSYS_BASE + 0x550)
+#define CLK_CFG_15_SET          (CKSYS_BASE + 0x554)
+#define CLK_CFG_15_CLR          (CKSYS_BASE + 0x558)
+#define CLK_CFG_16              (CKSYS_BASE + 0x560)
+#define CLK_CFG_16_SET          (CKSYS_BASE + 0x564)
+#define CLK_CFG_16_CLR          (CKSYS_BASE + 0x568)
+#define CLK_CFG_17              (CKSYS_BASE + 0x570)
+#define CLK_CFG_17_SET          (CKSYS_BASE + 0x574)
+#define CLK_CFG_17_CLR          (CKSYS_BASE + 0x578)
+
+/* INFRASYS Register, Infra DCM */
+#define TOP_CKMUXSEL            (INFRACFG_BASE + 0x000)
+#define TOP_CKDIV1              (INFRACFG_BASE + 0x008)
+#define TOP_DCMCTL              (INFRACFG_BASE + 0x010)
+
+#define INFRA_GLOBALCON_DCMCTL  (INFRACFG_BASE + 0x050)
+#define INFRA_GLOBALCON_DCMDBC  (INFRACFG_BASE + 0x054)
+#define INFRA_GLOBALCON_DCMFSEL (INFRACFG_BASE + 0x058)
+
+#define INFRA_APB_ASYNC_STA     (INFRACFG_BASE + 0x230)
+
+/* MCUCFG CLKMUX */
+#define mp0_pll_divider_cfg     (MCUSYS_CFGREG_BASE + 0x7A0)
+#define mp2_pll_divider_cfg     (MCUSYS_CFGREG_BASE + 0x7A8)
+#define bus_pll_divider_cfg     (MCUSYS_CFGREG_BASE + 0x7C0)
+
+/* SUBSYS_CG */
+#define INFRA_PDN_SET           (INFRACFG_BASE + 0x040)
+#define INFRA_PDN_CLR           (INFRACFG_BASE + 0x044)
+#define DEVAPC_PDN_SET          (INFRACFG_BASE + 0x070)
+#define TRNG_PDN_SET            (INFRACFG_BASE + 0x080)
+#define TRNG_PDN_CLR            (INFRACFG_BASE + 0x084)
+#define TRNG_PDN_STATUS         (INFRACFG_BASE + 0x088)
+#define PERI_GLOBALCON_PDN0_SET (PERICFG_BASE + 0x008)
+#define PERI_GLOBALCON_PDN1_SET (PERICFG_BASE + 0x00C)
+#define PERI_GLOBALCON_PDN0_CLR (PERICFG_BASE + 0x010)
+#define PERI_GLOBALCON_PDN1_CLR (PERICFG_BASE + 0x014)
+#define PERI_MSDC_CLK_EN        (PERICFG_BASE + 0x42C)
+#define MFG_CG_CLR              (MFGCFG_BASE + 0x8)
+#define MMSYS_CG0_SET           (MMSYS_BASE + 0x104)
+#define MMSYS_CG0_CLR           (MMSYS_BASE + 0x108)
+#define MMSYS_CG1_CLR           (MMSYS_BASE + 0x118)
+#define MMSYS_CG2_CLR           (MMSYS_BASE + 0x228)
+#define IMG_CG                  (IMGSYS_BASE + 0x0)
+#define BDP_DISPSYS_CG_CON0     (BDPSYS_BASE + 0x100)
+#define VDEC_CKEN_SET           (VDECSYS_BASE + 0x0)
+#define VDEC_LARB1_CKEN_SET     (VDECSYS_BASE + 0x8)
+#define VENC_CG_SET             (VENCSYS_BASE + 0x4)
+#define JPGDEC_CG_SET           (JPGDECSYS_BASE + 0x4)
+
+void mt_pll_init(void);
+void mt_pll_post_init(void);
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/pmic.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/pmic.h
new file mode 100644
index 0000000..abad914
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/pmic.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2020 MediaTek Inc.
+ *
+ * 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
+ */
+
+#pragma once
+
+int rtq2134_set_bcpu_voltage(int target_vol);
+int rtq2134_set_lcpu_voltage(int target_vol);
+int rtq2134_set_core_voltage(int target_vol);
\ No newline at end of file
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/ram_console_def.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/ram_console_def.h
new file mode 100644
index 0000000..d3349a3
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/ram_console_def.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __RAM_CONSOLE_DEF_H__
+#define __RAM_CONSOLE_DEF_H__
+
+#include <platform/mt2712.h>
+
+// ram_console over dram for mt2712
+
+#ifdef RAM_CONSOLE_OVER_SRAM  // sram
+#define RAM_CONSOLE_DEF_ADDR RAM_CONSOLE_SRAM_ADDR
+#define RAM_CONSOLE_DEF_SIZE RAM_CONSOLE_SRAM_SIZE
+
+#else  // dram
+#define RAM_CONSOLE_DRAM_ADDR (DRAM_BASE_PHY + 0x38072000)
+#define RAM_CONSOLE_DRAM_SIZE (0x10000)
+
+#define RAM_CONSOLE_DEF_ADDR RAM_CONSOLE_DRAM_ADDR
+#define RAM_CONSOLE_DEF_SIZE RAM_CONSOLE_DRAM_SIZE
+#endif
+
+// align minirdump-reserved-memory in dts
+#define KE_RESERVED_MEM_ADDR (DRAM_BASE_PHY + 0x38162000)
+#define PSTORE_ADDR (DRAM_BASE_PHY + 0x38082000)
+#define PSTORE_SIZE 0xe0000
+
+#define PSTORE_RESERVE_ADDR PSTORE_ADDR
+#define PSTORE_RESERVE_SIZE PSTORE_SIZE
+#define PSTORE_PMSG_SIZE (0X40000)
+#define PSTORE_CONSOEL_SIZE (0X10000)
+
+#define MINIRDUMP_MEM_ADDR KE_RESERVED_MEM_ADDR
+#define MINIRDUMP_MEM_SIZE (0x10000)
+
+#define LOG_STORE_MEM_ADDR (DRAM_BASE_PHY + 0X38032000)
+#define LOG_STORE_MEM_SIZE (0X40000)
+
+#endif
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/sej.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/sej.h
new file mode 100644
index 0000000..8da7f4a
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/sej.h
@@ -0,0 +1,50 @@
+/* Copyright Statement:
+*
+* This software/firmware and related documentation ("MediaTek Software") are
+* protected under relevant copyright laws. The information contained herein
+* is confidential and proprietary to MediaTek Inc. and/or its licensors.
+* Without the prior written permission of MediaTek inc. and/or its licensors,
+* any reproduction, modification, use or disclosure of MediaTek Software,
+* and information contained herein, in whole or in part, shall be strictly prohibited.
+*
+* MediaTek Inc. (C) 2017. All rights reserved.
+*
+* BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+* THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+* RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+* AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+* NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+* SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+* SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+* THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+* THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+* CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+* SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+* STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+* CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+* AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+* OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+* MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+*
+* The following software/firmware and/or related documentation ("MediaTek Software")
+* have been modified by MediaTek Inc. All revisions are subject to any receiver\'s
+* applicable license agreements with MediaTek Inc.
+*/
+
+#ifndef SEJ_H
+#define SEJ_H
+
+#include <platform/mt_reg_base.h>
+
+/******************************************************************************
+ * EXPORT FUNCTION
+ ******************************************************************************/
+extern int sp_sej_ac_init(unsigned char mode);
+extern unsigned char *sp_sej_ac_run(unsigned char *buf, unsigned int size);
+extern int sp_sej_ac_done(void);
+extern unsigned int seclib_get_msg_auth_key(unsigned char *id, unsigned int id_size,
+unsigned char *key, unsigned int key_size);
+#endif /* SEJ_H */
+
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/spm_mtcmos.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/spm_mtcmos.h
new file mode 100644
index 0000000..f26432f
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/spm_mtcmos.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once
+
+void spm_mtcmos_poweron_config_enable(void);
+void spm_mtcmos_protect_mfg_bus(void);
+void spm_mtcmos_display_power_on(void);
+void spm_mtcmos_audio_power_on(void);
+void spm_mtcmos_mfg_power_on(void);
+void spm_mtcmos_mfg_sc1_power_on(void);
+void spm_mtcmos_mfg_sc2_power_on(void);
+void spm_mtcmos_mfg_sc3_power_on(void);
+void spm_mtcmos_isp_power_on(void);
+void spm_mtcmos_vdec_power_on(void);
+void spm_mtcmos_venc_power_on(void);
+void spm_mtcmos_usb_power_on(void);
+void spm_mtcmos_usb2_power_on(void);
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/trapping.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/trapping.h
new file mode 100644
index 0000000..bd440ef
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/trapping.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once
+
+#include <platform/pll.h>   /* for CKGEN_BACKUP register definition */
+
+/* These trappings were referenced by Azalea BootROM to adjust boot
+ * configuration, add them here for LK reference */
+
+#define TRAPPING_SPI_NOR_4BYTE_ADDR_EN  (0x1 << 0)
+#define TRAPPING_BOOTDEV_SPEEDUP_EN     (0x1 << 1)
+#define TRAPPING_NAND_RANDOMIZER_EN     (0x1 << 2)
+
+#define TRAPPING_IS_SPI_NOR_4BYTE_ADDR_EN() \
+        (readl(CKGEN_BACKUP) & TRAPPING_SPI_NOR_4BYTE_ADDR_EN)
+
+#define TRAPPING_IS_EMMC_BOOT_BUS_WIDTH_8BIT() \
+        (readl(CKGEN_BACKUP) & TRAPPING_BOOTDEV_SPEEDUP_EN)
+
+#define TRAPPING_IS_NAND_RANDOMIZER_EN() \
+        (readl(CKGEN_BACKUP) & TRAPPING_NAND_RANDOMIZER_EN)
+
diff --git a/src/bsp/lk/platform/mediatek/mt2712/include/platform/udc-common.h b/src/bsp/lk/platform/mediatek/mt2712/include/platform/udc-common.h
new file mode 100644
index 0000000..1347794
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/include/platform/udc-common.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#pragma once
+
+#define UDC_TYPE_BULK_IN    1
+#define UDC_TYPE_BULK_OUT   2
diff --git a/src/bsp/lk/platform/mediatek/mt2712/plat_mp.c b/src/bsp/lk/platform/mediatek/mt2712/plat_mp.c
new file mode 100644
index 0000000..57c9313
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/plat_mp.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020 MediaTek Inc.
+ *
+ * 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/mp_mediatek.h>
+#include <kernel/mp.h>
+#include <kernel/thread.h>
+#include <platform/mtk_devinfo.h>
+#include <platform/pmic.h>
+#include <sys/types.h>
+#include <trace.h>
+
+#define LOCAL_TRACE 0
+
+/* mt2712 cpu id remapping table */
+const uint8_t mt2712_cpuid_map[] = {
+    0, 1, 2, 3,             // cluster 0 (little cores cluster)
+    0xff, 0xff, 0xff, 0xff, // cluster 1 (empty cluster)
+    4, 5                    // cluster 2 (big cores cluster)
+};
+
+const uint8_t *linear_cpuid_map = &mt2712_cpuid_map[0];
+
+struct smp_cpu_info smp_cpu = {
+    .id = { 0x0, 0x1, 0x2, 0x3, 0x200, 0x201 },
+    .cpu_on_mask = SMP_CPU_ON_MASK,
+};
+
+void plat_mp_assign_workcpu(thread_t *t)
+{
+    uint32_t worker_cpu;
+    uint32_t bootcpu = arch_curr_cpu_num();
+    uint32_t ca72_mask = smp_cpu.cpu_on_mask &
+        ~((1 << (1 << SMP_CPU_CLUSTER_SHIFT)) - 1);
+
+    ca72_mask &= ~(1 << bootcpu);
+    worker_cpu = bootcpu;
+    if (ca72_mask) {
+        worker_cpu = __builtin_ffs(ca72_mask) - 1;
+        if (!mp_is_cpu_active(worker_cpu))
+            worker_cpu = bootcpu;
+    }
+    thread_set_pinned_cpu(t, worker_cpu);
+}
+
+void plat_pre_cpu_on(void)
+{
+    int ca72_freq;
+
+    ca72_freq = det_ca72_freq();
+
+    /* The voltage is set to 1.05V when CA72 freq is higher than 1.4GHz. */
+    switch (ca72_freq) {
+        case CA72_SPD_1599MHZ:
+        case CA72_SPD_1495MHZ:
+        case CA72_SPD_1391MHZ: // treat as 1.4GHz
+            rtq2134_set_bcpu_voltage(1050);
+            spin(10); // delay 10us
+            break;
+    }
+}
diff --git a/src/bsp/lk/platform/mediatek/mt2712/platform.c b/src/bsp/lk/platform/mediatek/mt2712/platform.c
new file mode 100644
index 0000000..ef9d252
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/platform.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#include <compiler.h>
+#include <debug.h>
+#include <dev/interrupt/arm_gic.h>
+#include <dev/timer/arm_generic.h>
+#include <dev/uart.h>
+#include <err.h>
+#include <errno.h>
+#include <lib/mempool.h>
+#include <lk/init.h>
+#include <platform.h>
+#include <platform/platform_blx.h>
+#include <platform/gic.h>
+#include <platform/mtk_wdt.h>
+#include <platform/mt_gpio.h>
+#include <platform/mt_scp.h>
+#include <arch/ops.h>
+
+#if WITH_SMP
+#include <arch/mp_mediatek.h>
+#endif
+
+#if LOG_STORE_SUPPORT
+#include <lib/log_store.h>
+#endif
+
+void platform_early_init(void)
+{
+    uart_init_early();
+
+#if (LOG_STORE_SUPPORT == 1)
+    log_store_init(MEMORY_LOG_PHYS, MEMORY_LOG_SIZE);
+#endif
+
+    /* initialize the interrupt controller */
+    arm_gic_init();
+
+    arm_generic_timer_init(ARM_GENERIC_TIMER_PHYSICAL_INT, 13000000);
+
+    extern void  mtk_wdt_early_init(void) __attribute__((weak));
+    if (mtk_wdt_early_init)
+        mtk_wdt_early_init();
+    /* init AP watchdog and set timeout to 10 secs */
+    mtk_wdt_init();
+
+    /* init gpio */
+    mt_gpio_init();
+
+    /* bl2 or bl33 specific platform early init */
+    platform_early_init_blx();
+}
+
+void platform_init(void)
+{
+    int cache_ret, uncache_ret;
+
+    cache_ret = NO_ERROR;
+    if (!!CACHED_MEMPOOL_ADDR && !!CACHED_MEMPOOL_SIZE)
+        cache_ret = mempool_init((void *)CACHED_MEMPOOL_ADDR,
+                                 CACHED_MEMPOOL_SIZE, MEMPOOL_CACHE);
+
+    uncache_ret = NO_ERROR;
+    if (!!UNCACHED_MEMPOOL_ADDR && !!UNCACHED_MEMPOOL_SIZE)
+        uncache_ret = mempool_init((void *)UNCACHED_MEMPOOL_ADDR,
+                                   UNCACHED_MEMPOOL_SIZE, MEMPOOL_UNCACHE);
+
+    if ((cache_ret != NO_ERROR) || (uncache_ret != NO_ERROR))
+        platform_halt(HALT_ACTION_REBOOT, HALT_REASON_SW_PANIC);
+}
+
+void platform_halt(platform_halt_action suggested_action,
+                   platform_halt_reason reason)
+{
+    switch (suggested_action) {
+        case HALT_ACTION_REBOOT:
+            dprintf(ALWAYS, "REBOOT (reason = %d)\n",reason);
+            mtk_arch_reset(1);
+            break;
+        default:
+            break;
+    }
+    dprintf(ALWAYS, "HALT: spinning forever... (reason = %d)\n", reason);
+    arch_disable_ints();
+    for (;;);
+}
+
+#if WITH_SMP
+void platform_quiesce(void)
+{
+    plat_mp_off();
+}
+#endif
+
+/*
+ * target_ab_set_active_bootdev() - set active boot device in ab boot flow
+ *
+ * this function is used to set the active boot device, aka slot in ab boot
+ * concept. For mt2712, the slot concept should be defined by the actual
+ * target. For targets that support ab boot concept should override this
+ * function.
+ *
+ * @slot: the slot # to be set as active boot device
+ *
+ * returns:
+ *     -ENOTSUP: function not supported
+ */
+__WEAK int target_ab_set_active_bootdev(int slot)
+{
+    return -ENOTSUP;
+}
+
+/*
+ * override the common plat_ab_set_active_bootdev() impl.
+ */
+int plat_ab_set_active_bootdev(int slot)
+{
+    return target_ab_set_active_bootdev(slot);
+}
+
+/* override blxboot plat_start_scpsys() impl. to start scpsys */
+void plat_start_scpsys(void)
+{
+    extern __WEAK void start_scpsys(void);
+
+    start_scpsys();
+}
+
+/* provide hook function called in blxboot, do something */
+static void plat_app_early_init(uint level)
+{
+#if (LOG_STORE_SUPPORT == 1)
+    log_store_switch_buffer(MEMORY_LOG_PHYS, DRAM_LOG_ADDR, DRAM_LOG_SIZE);
+#endif
+}
+
+LK_INIT_HOOK(plat_app_early_init, &plat_app_early_init, LK_INIT_LEVEL_APPS - 1);
diff --git a/src/bsp/lk/platform/mediatek/mt2712/platform_bl2.c b/src/bsp/lk/platform/mediatek/mt2712/platform_bl2.c
new file mode 100644
index 0000000..4600529
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/platform_bl2.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#include <arch/ops.h>
+#include <boot_args.h>
+#include <debug.h>
+#include <err.h>
+#include <lk/init.h>
+#if WITH_KERNEL_VM
+#include <kernel/vm.h>
+#else
+#include <kernel/novm.h>
+#endif
+#include <memory.h>
+#include <platform.h>
+#include <platform/audio_clk_enable.h>
+#include <platform/dcm.h>
+#include <platform/mt_infracfg.h>
+#include <platform/mt_pericfg.h>
+#include <platform/mtk_wdt.h>
+#include <platform/pll.h>
+#include <string.h>
+#include <sys/types.h>
+#include <trace.h>
+
+#define LOCAL_TRACE 0
+
+extern __WEAK void stop_scpsys(void);
+
+static uint32_t g_boot_reason = 0;
+static uint32_t g_rgu_mode = 0;
+
+#if WITH_KERNEL_VM
+#define PROG_MEM_MAPPING_IDX    0
+#define CHIP_ID_MAPPING_IDX     1
+#define PERIPHERAL_MAPPING_IDX  2
+#define SRAM_MAPPING_IDX        3
+#define CMSYS_SRAM_MAPPING_IDX  4
+#define DRAM_MAPPING_IDX        5
+
+/* initial memory mappings. parsed by start.S */
+struct mmu_initial_mapping mmu_initial_mappings[] = {
+    {
+        .phys = MEMORY_BASE_PHYS,
+        .virt = MEMORY_BASE_VIRT,
+        .size = MEMORY_APERTURE_SIZE,
+        .flags = 0,
+        .name = "prog",
+    },
+    {
+        .phys = CHIP_ID_BASE_PHYS,
+        .virt = CHIP_ID_BASE_VIRT,
+        .size = CHIP_ID_BASE_SIZE,
+        .flags = MMU_INITIAL_MAPPING_FLAG_DEVICE
+    },
+    {
+        .phys = PERIPHERAL_BASE_PHYS,
+        .virt = PERIPHERAL_BASE_VIRT,
+        .size = PERIPHERAL_BASE_SIZE,
+        .flags = MMU_INITIAL_MAPPING_FLAG_DEVICE,
+        .name = "peripherals"
+    },
+
+    /* reserved for internal sram */
+    { .size = 0 },
+    /* reserved for cmsys sram */
+    { .size = 0 },
+    /* reserved for dram */
+    { .size = 0 },
+    /* null entry to terminate the list */
+    { .size = 0 }
+};
+
+static pmm_arena_t arena = {
+    .name = "sram",
+    .base = SRAM_ARENA_BASE,
+    .size = SRAM_ARENA_SIZE,
+    .flags = PMM_ARENA_FLAG_KMAP,
+    .priority = 1,
+};
+
+static pmm_arena_t dram_arena = {
+    .name = "dram",
+    .base = DRAM_ARENA_BASE,
+    .size = DRAM_ARENA_SIZE,
+    .flags = PMM_ARENA_FLAG_KMAP,
+    .priority = 0,
+};
+#endif /* WITH_KERNEL_VM */
+
+static inline bool is_4gb_dram(size_t dram_sz)
+{
+#define SIZE_GB (0x40000000ULL)
+
+    return ((SIZE_GB * 4) == dram_sz);
+}
+
+static inline size_t query_plat_dram_sz(void)
+{
+    return mt_mem_size();
+}
+
+static void setup_plat_mem(void)
+{
+#if WITH_KERNEL_VM
+    size_t dram_sz, mem_sz;
+    size_t unmap_dram_start_pa, unmap_dram_start_va;
+
+    arch_mmu_map(CMSYS_SRAM_VIRT, CMSYS_SRAM_PHYS,
+                 CMSYS_SRAM_SIZE >> PAGE_SIZE_SHIFT,
+                 ARCH_MMU_FLAG_UNCACHED);
+
+    /* add cmsys sram to mmu_initial_mappings struct for pa to va lookup */
+    mmu_initial_mappings[CMSYS_SRAM_MAPPING_IDX].phys = CMSYS_SRAM_PHYS;
+    mmu_initial_mappings[CMSYS_SRAM_MAPPING_IDX].virt = CMSYS_SRAM_VIRT;
+    mmu_initial_mappings[CMSYS_SRAM_MAPPING_IDX].size = CMSYS_SRAM_SIZE;
+    mmu_initial_mappings[CMSYS_SRAM_MAPPING_IDX].flags = 0;
+    mmu_initial_mappings[CMSYS_SRAM_MAPPING_IDX].name = "cmsys";
+
+    /*
+     * Except the dram range for uncached memory pool, all other dram ranges
+     * are all mapped as cacheable memory.
+     */
+    unmap_dram_start_pa = DRAM_BASE_PHY;
+
+    unmap_dram_start_va = DRAM_BASE_VIRT;
+
+    dram_sz = query_plat_dram_sz();
+    mem_sz = UNCACHED_MEMPOOL_ADDR - unmap_dram_start_va;
+    arch_mmu_map(unmap_dram_start_va, unmap_dram_start_pa,
+                 mem_sz >> PAGE_SIZE_SHIFT, ARCH_MMU_FLAG_CACHED);
+    arch_mmu_map(UNCACHED_MEMPOOL_ADDR, unmap_dram_start_pa + mem_sz,
+                 UNCACHED_MEMPOOL_SIZE >> PAGE_SIZE_SHIFT,
+                 ARCH_MMU_FLAG_UNCACHED);
+
+    mem_sz += UNCACHED_MEMPOOL_SIZE;
+    arch_mmu_map(UNCACHED_MEMPOOL_ADDR + UNCACHED_MEMPOOL_SIZE,
+                 unmap_dram_start_pa + mem_sz,
+                 (dram_sz - mem_sz) >> PAGE_SIZE_SHIFT, ARCH_MMU_FLAG_CACHED);
+
+    /* add dram to mmu_initial_mappings for pa to va lookup */
+    mmu_initial_mappings[DRAM_MAPPING_IDX].phys = DRAM_BASE_PHY;
+    mmu_initial_mappings[DRAM_MAPPING_IDX].virt = DRAM_BASE_VIRT;
+    mmu_initial_mappings[DRAM_MAPPING_IDX].size = dram_sz;
+    mmu_initial_mappings[DRAM_MAPPING_IDX].flags = 0;
+    mmu_initial_mappings[DRAM_MAPPING_IDX].name = "dram";
+
+    pmm_add_arena(&dram_arena);
+
+    if (is_4gb_dram(dram_sz)) {
+        *REG32(PERIAXI_BUS_CTL3) |= PERISYS_4G_SUPPORT;
+        *REG32(INFRA_MISC) |= DDR_4GB_SUPPORT_EN;
+    }
+#else
+    novm_add_arena("sram", SRAM_BASE_PHYS, SRAM_BASE_SIZE);
+#endif /* WITH_KERNEL_VM */
+}
+
+void *init_bootarg_buffer(void)
+{
+    void *bootarg = (void *)paddr_to_kvaddr(DRAM_BOOTARG_BASE);
+    memset((void *)bootarg, 0, DRAM_BOOTARG_SIZE);
+
+    return bootarg;
+}
+
+void *bl2_set_boot_args(uint32_t boot_mode)
+{
+    BOOT_ARGUMENT *bl2_bootarg = (BOOT_ARGUMENT *)init_bootarg_buffer();
+    extern u32 g_ddr_reserve_ready;
+    extern u32 g_ddr_reserve_ta_err;
+    extern u32 g_ddr_reserve_enable;
+
+    bl2_bootarg->maggic_number = BOOT_ARGUMENT_MAGIC;
+    bl2_bootarg->boot_mode  = boot_mode;
+    bl2_bootarg->boot_reason = g_boot_reason;
+    bl2_bootarg->rgu_mode = g_rgu_mode;
+    bl2_bootarg->ddr_reserve_enable = g_ddr_reserve_enable;
+    bl2_bootarg->ddr_reserve_success = (g_ddr_reserve_ta_err == 0) ? 1 : 0;
+    bl2_bootarg->ddr_reserve_ready = g_ddr_reserve_ready;
+    bl2_bootarg->dram_size = (u64)mt_mem_size();
+
+    LTRACEF("boot mode:%d boot_reason:%d rgu_mode:%d\n",
+                bl2_bootarg->boot_mode, bl2_bootarg->boot_reason, bl2_bootarg->rgu_mode);
+
+    LTRACEF("DDR reserve mode: enable = %d, success = %d, ready = %d\n",
+                bl2_bootarg->ddr_reserve_enable,
+                bl2_bootarg->ddr_reserve_success, bl2_bootarg->ddr_reserve_ready);
+    return (void *)bl2_bootarg;
+}
+
+void platform_memory_init(void)
+{
+    mt_mem_init();
+    setup_plat_mem();
+    mt_mem_test();
+}
+
+void platform_early_init_blx(void)
+{
+    /* in case scpsys is still running after sw reset, stop it */
+    stop_scpsys();
+    clr_cm4_resume_mode();
+
+    mt_pll_init();
+    mt_pll_post_init();
+
+    /* TODO: confirm audio clk enable in BL2 or BL33 */
+    mt_audio_clk_enable();
+
+    mt_dcm_init();
+
+    dprintf(CRITICAL, "BL2 Build Time: %s %s\n", __DATE__, __TIME__);
+}
+
+/* the function provide interface to do some thing in the begin to platform early init */
+static void platform_prepare_early_init(uint level)
+{
+#if WITH_KERNEL_VM
+    /*
+     * At this moment, arena memory was not added yet, so the arena memory to
+     * be added to mmu table must use existing page, otherwise the mmu map will
+     * fail. And the memory is needed becasue log store need it to store log and it
+     * need be ready before uart init done.
+     */
+    arch_mmu_map(SRAM_BASE_VIRT, SRAM_BASE_PHYS,
+                 SRAM_BASE_SIZE >> PAGE_SIZE_SHIFT, ARCH_MMU_FLAG_CACHED);
+
+    /* add internal sram to mmu_initial_mappings struct for pa to va lookup */
+    mmu_initial_mappings[SRAM_MAPPING_IDX].phys = SRAM_BASE_PHYS;
+    mmu_initial_mappings[SRAM_MAPPING_IDX].virt = SRAM_BASE_VIRT;
+    mmu_initial_mappings[SRAM_MAPPING_IDX].size = SRAM_BASE_SIZE;
+    mmu_initial_mappings[SRAM_MAPPING_IDX].flags = 0;
+    mmu_initial_mappings[SRAM_MAPPING_IDX].name = "isram";
+
+    /* map dram may need new page tables, add internal sram to arena memory */
+    pmm_add_arena(&arena);
+
+#endif /* WITH_KERNEL_VM */
+}
+
+LK_INIT_HOOK(plat_prepare_early_init,
+        &platform_prepare_early_init, LK_INIT_LEVEL_PLATFORM_EARLY - 1);
+
+void mtk_wdt_early_init(void)
+{
+    /* now the dram not init done, can not use dram to cache g_bootarg */
+    check_ddr_reserve_status();
+    g_boot_reason = mtk_wdt_check_status();
+    g_rgu_mode = readl(MTK_WDT_MODE);
+    LTRACEF("g_boot_reason:0x%x g_rgu_mode:0x%x\n", g_boot_reason, g_rgu_mode);
+}
+
+extern int write_efuse(unsigned int index, const unsigned char *data, unsigned int len)__attribute__((weak));
+extern void enable_vefuse(void)__attribute__((weak));
+extern void disable_vefuse(void)__attribute__((weak));
+
+int plat_write_lk_antirollback_version(int version)
+{
+    uint32_t write_data = 0;
+    int ret = 0;
+    #define PL_VER_INDEX 27
+
+    if (!write_efuse || !enable_vefuse || !disable_vefuse) {
+        LTRACEF("efuse read/write function is not supported");
+        return -1;
+    }
+
+    if (version < 1 || version > 16) {
+        LTRACEF("lk version (%d)not support", version);
+        return -1;
+    }
+
+    write_data = (1 << (version - 1)) | (1 << (version - 1 + 16));
+
+    enable_vefuse();
+
+    ret = write_efuse(PL_VER_INDEX, (const unsigned char *)(&write_data), 4);
+
+    disable_vefuse();
+    return ret;
+}
+
diff --git a/src/bsp/lk/platform/mediatek/mt2712/platform_bl33.c b/src/bsp/lk/platform/mediatek/mt2712/platform_bl33.c
new file mode 100644
index 0000000..31f9062
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/platform_bl33.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#include <arch/ops.h>
+#include <boot_args.h>
+#include <debug.h>
+#include <err.h>
+#if WITH_KERNEL_VM
+#include <kernel/vm.h>
+#else
+#include <kernel/novm.h>
+#endif
+#include <lib/kcmdline.h>
+#include <platform.h>
+#include <platform/mt2712.h>
+#include <platform/mtk_wdt.h>
+#include <platform/platform_blx.h>
+#include <trace.h>
+
+#define LOCAL_TRACE 0
+
+#if WITH_KERNEL_VM
+#define PROG_MEM_MAPPING_IDX    0
+#define CHIP_ID_MAPPING_IDX     1
+#define PERIPHERAL_MAPPING_IDX  2
+#define SRAM_MAPPING_IDX        3
+#define DRAM_MAPPING_IDX        4
+
+BOOT_ARGUMENT *g_boot_arg;
+extern ulong lk_boot_args[4];
+
+/* initial memory mappings. parsed by start.S */
+struct mmu_initial_mapping mmu_initial_mappings[] = {
+    {
+        .phys = MEMORY_BASE_PHYS,
+        .virt = MEMORY_BASE_VIRT,
+        .size = MEMORY_APERTURE_SIZE,
+        .flags = 0,
+        .name = "prog"
+    },
+    {
+        .phys = CHIP_ID_BASE_PHYS,
+        .virt = CHIP_ID_BASE_VIRT,
+        .size = CHIP_ID_BASE_SIZE,
+        .flags = MMU_INITIAL_MAPPING_FLAG_DEVICE
+    },
+    {
+        .phys = PERIPHERAL_BASE_PHYS,
+        .virt = PERIPHERAL_BASE_VIRT,
+        .size = PERIPHERAL_BASE_SIZE,
+        .flags = MMU_INITIAL_MAPPING_FLAG_DEVICE,
+        .name = "peripherals"
+    },
+
+    /* reserved for internal sram */
+    { .size = 0 },
+    /* reserved for dram */
+    { .size = 0 },
+    /* null entry to terminate the list */
+    { .size = 0 }
+};
+
+static pmm_arena_t dram_arena = {
+    .name = "dram",
+    .base = DRAM_ARENA_BASE,
+    .size = DRAM_ARENA_SIZE,
+    .flags = PMM_ARENA_FLAG_KMAP,
+    .priority = 0,
+};
+#endif /* WITH_KERNEL_VM */
+
+static inline size_t query_plat_dram_sz(void)
+{
+    return g_boot_arg->dram_size;
+}
+
+static void setup_plat_mem(void)
+{
+#if WITH_KERNEL_VM
+    size_t unmap_dram_sz, mapped_mem_sz;
+    size_t unmap_dram_start_pa, unmap_dram_start_va;
+
+    /* dram arena memory are already mapped in mmu initial mappings */
+    pmm_add_arena(&dram_arena);
+
+    arch_mmu_map(SRAM_BASE_VIRT, SRAM_BASE_PHYS,
+                 SRAM_BASE_SIZE >> PAGE_SIZE_SHIFT, ARCH_MMU_FLAG_CACHED);
+
+    /* add internal sram to mmu_initial_mappings for pa to va lookup */
+    mmu_initial_mappings[SRAM_MAPPING_IDX].phys = SRAM_BASE_PHYS;
+    mmu_initial_mappings[SRAM_MAPPING_IDX].virt = SRAM_BASE_VIRT;
+    mmu_initial_mappings[SRAM_MAPPING_IDX].size = SRAM_BASE_SIZE;
+    mmu_initial_mappings[SRAM_MAPPING_IDX].flags = 0;
+    mmu_initial_mappings[SRAM_MAPPING_IDX].name = "sram";
+
+    /*
+     * Except the dram range for uncached memory pool, all other dram ranges
+     * are all mapped as cacheable memory.
+     */
+
+    /* BL33 initial mapping already maps a portion of dram, map the rest here */
+    unmap_dram_start_pa = MEMORY_BASE_PHYS + MEMORY_APERTURE_SIZE;
+    unmap_dram_start_va = MEMORY_BASE_VIRT + MEMORY_APERTURE_SIZE;
+
+    mapped_mem_sz = UNCACHED_MEMPOOL_ADDR - unmap_dram_start_va;
+    arch_mmu_map(unmap_dram_start_va, unmap_dram_start_pa,
+                 mapped_mem_sz >> PAGE_SIZE_SHIFT, ARCH_MMU_FLAG_CACHED);
+    arch_mmu_map(UNCACHED_MEMPOOL_ADDR, unmap_dram_start_pa + mapped_mem_sz,
+                 UNCACHED_MEMPOOL_SIZE >> PAGE_SIZE_SHIFT,
+                 ARCH_MMU_FLAG_UNCACHED);
+
+    unmap_dram_sz = query_plat_dram_sz() - MEMORY_APERTURE_SIZE;
+    mapped_mem_sz += UNCACHED_MEMPOOL_SIZE;
+    arch_mmu_map(UNCACHED_MEMPOOL_ADDR + UNCACHED_MEMPOOL_SIZE,
+                 unmap_dram_start_pa + mapped_mem_sz,
+                 (unmap_dram_sz - mapped_mem_sz) >> PAGE_SIZE_SHIFT,
+                 ARCH_MMU_FLAG_CACHED);
+
+    /* add dram to mmu_initial_mappings for pa to va lookup */
+    mmu_initial_mappings[DRAM_MAPPING_IDX].phys = unmap_dram_start_pa;
+    mmu_initial_mappings[DRAM_MAPPING_IDX].virt = unmap_dram_start_va;
+    mmu_initial_mappings[DRAM_MAPPING_IDX].size = unmap_dram_sz;
+    mmu_initial_mappings[DRAM_MAPPING_IDX].flags = 0;
+    mmu_initial_mappings[DRAM_MAPPING_IDX].name = "dram";
+#else
+    novm_add_arena("sram", SRAM_BASE_PHYS, SRAM_BASE_SIZE);
+#endif /* WITH_KERNEL_VM */
+}
+
+void platform_get_bootargs(void)
+{
+    g_boot_arg  = (BOOT_ARGUMENT *)paddr_to_kvaddr(lk_boot_args[0]);
+    if((g_boot_arg == NULL) ||
+        (*(unsigned int *)g_boot_arg != BOOT_ARGUMENT_MAGIC)) {
+        dprintf(CRITICAL, "%s failed (g_boot_arg:%p)\n", __func__, g_boot_arg);
+        g_boot_arg = NULL;
+        return;
+    }
+
+    dprintf(ALWAYS, "%s pa:0x%lx va:%p dram size:0x%llx\n",
+            __func__, lk_boot_args[0], g_boot_arg, g_boot_arg->dram_size);
+}
+
+void platform_memory_init(void)
+{
+    /* bootargs memory should be included in initial mmu mapping */
+    platform_get_bootargs();
+    setup_plat_mem();
+}
+
+void platform_early_init_blx(void)
+{
+    dprintf(CRITICAL, "BL33 Build Time: %s %s\n", __DATE__, __TIME__);
+}
+
+int platform_wdt_boot_check(void)
+{
+    unsigned int wdt_sta = g_boot_arg->boot_reason;
+    unsigned int rgu_mode = g_boot_arg->rgu_mode;
+
+    if (wdt_sta & (MTK_WDT_STATUS_HWWDT_RST|MTK_WDT_STATUS_SWWDT_RST|MTK_WDT_STATUS_SPMWDT_RST)) {
+        if (rgu_mode & MTK_WDT_MODE_AUTO_RESTART)
+        {
+            /* HW/SW reboot, and auto restart is set, means bypass power key */
+            LTRACEF("SW reset with bypass power key flag\n");
+            return WDT_BY_PASS_PWK_REBOOT;
+        } else {
+            LTRACEF("SW reset without bypass power key flag\n");
+            return WDT_NORMAL_REBOOT;
+        }
+    }
+    return WDT_NOT_WDT_REBOOT;
+}
+
+boot_reason_t platform_boot_status(void)
+{
+    if (platform_wdt_boot_check() == WDT_NORMAL_REBOOT) {
+        LTRACEF("WDT normal boot!\n");
+        return BR_WDT;
+    } else if(platform_wdt_boot_check() == WDT_BY_PASS_PWK_REBOOT) {
+        LTRACEF("WDT reboot bypass power key!\n");
+        return BR_WDT_BY_PASS_PWK;
+    }
+    dprintf(CRITICAL, "BL33 check Boot status-WDT\n");
+
+    /*TODO implement rtc_boot_check*/
+    /*TODO implement mtk_detect_key*/
+    /*TODO implement usb_accessory_in*/
+    return BR_UNKNOWN;
+}
diff --git a/src/bsp/lk/platform/mediatek/mt2712/rules.mk b/src/bsp/lk/platform/mediatek/mt2712/rules.mk
new file mode 100644
index 0000000..67d8a28
--- /dev/null
+++ b/src/bsp/lk/platform/mediatek/mt2712/rules.mk
@@ -0,0 +1,152 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+COMMON_PLAT := $(LOCAL_DIR)/../common
+
+ARCH ?= arm64
+ARM_CPU ?= cortex-a53
+WITH_SMP ?= 0
+WITH_KERNEL_VM ?= 1
+
+LK_HEAP_IMPLEMENTATION ?= miniheap
+
+GLOBAL_INCLUDES += -I$(LK_TOP_DIR)/include \
+
+MODULE_SRCS += \
+    $(COMMON_PLAT)/debug.c \
+    $(COMMON_PLAT)/fastboot_oem_cmd/oem_ab_cmd.c \
+    $(COMMON_PLAT)/fastboot_oem_cmd/oem_mac_cmd.c \
+    $(COMMON_PLAT)/interrupts.c \
+    $(COMMON_PLAT)/boot_mode.c \
+    $(LOCAL_DIR)/boot_mode.c \
+    $(LOCAL_DIR)/fixup/plat_fixup.c \
+    $(LOCAL_DIR)/platform.c \
+
+# check bcb (bootloader control block) for recovery mode
+BCB_RECOVERY_SUPPORT ?= 0
+
+ifeq ($(WITH_SMP),1)
+SMP_MAX_CPUS := 6 # if we need only one big core, set this to 5
+SMP_CPU_CLUSTER_SHIFT := 2
+SMP_CPU_ON_MASK ?= 0x11 # mask for which cpu to be on
+
+MODULE_SRCS += \
+    $(COMMON_PLAT)/arch/$(ARCH)/mp.c \
+    $(LOCAL_DIR)/plat_mp.c
+
+GLOBAL_DEFINES += SMP_CPU_ON_MASK=$(SMP_CPU_ON_MASK)
+endif
+
+ifeq ($(LK_AS_BL33),1)
+MODULE_SRCS += \
+    $(COMMON_PLAT)/fastboot_oem_cmd/oem_efuse_cmd.c
+endif
+
+GLOBAL_INCLUDES += \
+    $(COMMON_PLAT)/include \
+
+MACH_TYPE := 2712
+
+ifeq ($(WITH_KERNEL_VM),1)
+KERNEL_ASPACE_BASE ?= 0xfffffff000000000
+KERNEL_ASPACE_SIZE ?= 0x0000000180000000
+MMU_IDENT_SIZE_SHIFT ?= 32
+endif
+
+SCRATCH_SIZE        := 0x02000000 # 32MB
+
+# mempool configuration
+ifeq ($(WITH_KERNEL_VM),1)
+CACHED_MEMPOOL_ADDR ?= 0xfffffff073800000
+CACHED_MEMPOOL_SIZE ?= 0x02800000 # 40MB
+UNCACHED_MEMPOOL_ADDR ?= 0xfffffff073600000
+UNCACHED_MEMPOOL_SIZE ?= 0x200000 # 2MB
+BL33_ADDR ?= 0xfffffff073500000
+else
+CACHED_MEMPOOL_ADDR ?= 0 # don't care this when mmu is off
+CACHED_MEMPOOL_SIZE ?= 0
+UNCACHED_MEMPOOL_ADDR ?= 0x73600000
+UNCACHED_MEMPOOL_SIZE ?= 0x0c800000 # 200MB
+BL33_ADDR ?= 0x73500000
+endif
+
+# if DEBUG is not 0, enable debug feature
+ifneq ($(DEBUG),0)
+LOG_STORE_SUPPORT ?= 1
+endif
+
+# LK build as BL2 or BL33 setting
+LK_AS_BL33 ?= 0
+
+# CA35 FREQ:
+# CA35_FREQ_806MHZ (0.8G), CA35_FREQ_1001MHZ (1.0G), CA35_FREQ_1196MHZ (1.2G)
+CA35_FREQ ?= CA35_FREQ_1196MHZ
+
+# CA72 FREQ:
+# CA72_FREQ_1001MHZ(1.0G), CA72_FREQ_1196MHZ(1.2G), CA72_FREQ_1391MHZ(1.4G),
+# CA72_FREQ_1495MHZ(1.5G), CA72_FREQ_1599MHZ(1.6G)
+CA72_FREQ ?= CA72_FREQ_1599MHZ
+
+MODULE_DEPS += \
+    dev/interrupt/arm_gic \
+    dev/timer/arm_generic \
+    lib/bio \
+    lib/fastboot \
+    lib/fdt \
+    lib/fit \
+    lib/kcmdline \
+    lib/partition \
+    lib/mempool
+
+ifeq ($(LOG_STORE_SUPPORT),1)
+MODULE_DEPS += \
+    lib/log_store
+
+GLOBAL_DEFINES += LOG_STORE_SUPPORT=$(LOG_STORE_SUPPORT)
+endif
+
+# if BOOTAPP is not specified elsewhere, and AVB is required, choose 'avbboot'
+ifeq ($(strip $(SECURE_BOOT_ENABLE)),yes)
+ifeq ($(strip $(SECURE_BOOT_TYPE)),avb)
+BOOTAPP ?= avbboot
+endif
+endif
+
+# otherwise, choose 'fitboot'
+BOOTAPP ?= fitboot
+
+MODULES += app/$(BOOTAPP)
+
+# RTC OSC Removal
+ifeq ($(strip $(MTK_CLK32K_EXT_REMOVAL_SUPPORT)),yes)
+GLOBAL_DEFINES += MTK_CLK32K_EXT_REMOVAL_SUPPORT=1
+endif
+
+GLOBAL_DEFINES += MTK_THERMAL_RESET_SUPPORT
+
+ifeq ($(WITH_KERNEL_VM),1)
+GLOBAL_DEFINES += MMU_IDENT_SIZE_SHIFT=$(MMU_IDENT_SIZE_SHIFT)
+else
+GLOBAL_DEFINES += NOVM_MAX_ARENAS=2
+endif
+
+GLOBAL_DEFINES += \
+    RAMBASE=$(RAMBASE) \
+    MACH_TYPE=$(MACH_TYPE) \
+    PLATFORM_SUPPORTS_PANIC_SHELL=1 \
+    WITH_NO_FP=1 \
+    CACHED_MEMPOOL_ADDR=$(CACHED_MEMPOOL_ADDR) \
+    CACHED_MEMPOOL_SIZE=$(CACHED_MEMPOOL_SIZE) \
+    UNCACHED_MEMPOOL_ADDR=$(UNCACHED_MEMPOOL_ADDR) \
+    UNCACHED_MEMPOOL_SIZE=$(UNCACHED_MEMPOOL_SIZE) \
+    BL33_ADDR=$(BL33_ADDR) \
+    $(CA35_FREQ) \
+    $(CA72_FREQ) \
+    LK_AS_BL33=$(LK_AS_BL33) \
+    BCB_RECOVERY_SUPPORT=$(BCB_RECOVERY_SUPPORT)
+
+LINKER_SCRIPT += \
+    $(BUILDDIR)/system-onesegment.ld
+
+include $(LOCAL_DIR)/bl2_bl33_options.mk
+include make/module.mk $(LOCAL_DIR)/drivers/rules.mk