feat(mt8196): add SPMI driver
Add SPMI and PMIF driver for PMIC communication
Change-Id: Iad1d90381d6dad6b3e92fd9d6a3ce02fa11d15f1
Signed-off-by: Hope Wang <hope.wang@mediatek.corp-partner.google.com>
diff --git a/plat/mediatek/drivers/spmi/mt8196/platform_pmif_spmi.c b/plat/mediatek/drivers/spmi/mt8196/platform_pmif_spmi.c
new file mode 100644
index 0000000..5aab856
--- /dev/null
+++ b/plat/mediatek/drivers/spmi/mt8196/platform_pmif_spmi.c
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2025, Mediatek Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <lib/utils_def.h>
+
+#include <drivers/spmi/pmif_common.h>
+#include <drivers/spmi/pmif_v1/pmif.h>
+#include <drivers/spmi/spmi_common.h>
+#include <drivers/spmi/spmi_sw.h>
+#include <drivers/spmi_api.h>
+#include <lib/mtk_init/mtk_init.h>
+#include <mtk_mmap_pool.h>
+
+#define SPMI_GROUP_ID 0xB
+#define SPMI_DEBUG 0
+
+static const mmap_region_t pmif_spmi_mmap[] MTK_MMAP_SECTION = {
+ MAP_REGION_FLAT(PMIF_SPMI_M_BASE, PMIF_SPMI_SIZE,
+ MT_DEVICE | MT_RW | MT_NS),
+ MAP_REGION_FLAT(SPMI_MST_M_BASE, SPMI_MST_SIZE,
+ MT_DEVICE | MT_RW | MT_SECURE),
+ MAP_REGION_FLAT(PMIF_SPMI_P_BASE, PMIF_SPMI_SIZE,
+ MT_DEVICE | MT_RW | MT_NS),
+ {0}
+};
+DECLARE_MTK_MMAP_REGIONS(pmif_spmi_mmap);
+
+static uint16_t mt6xxx_spmi_regs[] = {
+ [SPMI_OP_ST_CTRL] = 0x0000,
+ [SPMI_GRP_ID_EN] = 0x0004,
+ [SPMI_OP_ST_STA] = 0x0008,
+ [SPMI_MST_SAMPL] = 0x000C,
+ [SPMI_MST_REQ_EN] = 0x0010,
+ [SPMI_RCS_CTRL] = 0x0014,
+ [SPMI_SLV_3_0_EINT] = 0x0020,
+ [SPMI_SLV_7_4_EINT] = 0x0024,
+ [SPMI_SLV_B_8_EINT] = 0x0028,
+ [SPMI_SLV_F_C_EINT] = 0x002C,
+ [SPMI_REC_CTRL] = 0x0040,
+ [SPMI_REC0] = 0x0044,
+ [SPMI_REC1] = 0x0048,
+ [SPMI_REC2] = 0x004C,
+ [SPMI_REC3] = 0x0050,
+ [SPMI_REC4] = 0x0054,
+ [SPMI_REC_CMD_DEC] = 0x005C,
+ [SPMI_DEC_DBG] = 0x00F8,
+ [SPMI_MST_DBG] = 0x00FC,
+};
+
+static uint16_t mt6xxx_regs[] = {
+ [PMIF_INIT_DONE] = 0x0000,
+ [PMIF_INF_EN] = 0x0024,
+ [PMIF_ARB_EN] = 0x0150,
+ [PMIF_IRQ_EVENT_EN_0] = 0x0420,
+ [PMIF_IRQ_FLAG_0] = 0x0428,
+ [PMIF_IRQ_CLR_0] = 0x042C,
+ [PMIF_IRQ_EVENT_EN_2] = 0x0440,
+ [PMIF_IRQ_FLAG_2] = 0x0448,
+ [PMIF_IRQ_CLR_2] = 0x044C,
+ [PMIF_WDT_CTRL] = 0x0470,
+ [PMIF_WDT_EVENT_EN_1] = 0x047C,
+ [PMIF_WDT_FLAG_1] = 0x0480,
+ [PMIF_SWINF_2_ACC] = 0x0880,
+ [PMIF_SWINF_2_WDATA_31_0] = 0x0884,
+ [PMIF_SWINF_2_WDATA_63_32] = 0x0888,
+ [PMIF_SWINF_2_RDATA_31_0] = 0x0894,
+ [PMIF_SWINF_2_RDATA_63_32] = 0x0898,
+ [PMIF_SWINF_2_VLD_CLR] = 0x08A4,
+ [PMIF_SWINF_2_STA] = 0x08A8,
+ [PMIF_SWINF_3_ACC] = 0x08C0,
+ [PMIF_SWINF_3_WDATA_31_0] = 0x08C4,
+ [PMIF_SWINF_3_WDATA_63_32] = 0x08C8,
+ [PMIF_SWINF_3_RDATA_31_0] = 0x08D4,
+ [PMIF_SWINF_3_RDATA_63_32] = 0x08D8,
+ [PMIF_SWINF_3_VLD_CLR] = 0x08E4,
+ [PMIF_SWINF_3_STA] = 0x08E8,
+ /* hw mpu */
+ [PMIF_PMIC_ALL_RGN_EN_1] = 0x09B0,
+ [PMIF_PMIC_ALL_RGN_EN_2] = 0x0D30,
+ [PMIF_PMIC_ALL_RGN_0_START] = 0x09B4,
+ [PMIF_PMIC_ALL_RGN_0_END] = 0x09B8,
+ [PMIF_PMIC_ALL_RGN_1_START] = 0x09BC,
+ [PMIF_PMIC_ALL_RGN_1_END] = 0x09C0,
+ [PMIF_PMIC_ALL_RGN_2_START] = 0x09C4,
+ [PMIF_PMIC_ALL_RGN_2_END] = 0x09C8,
+ [PMIF_PMIC_ALL_RGN_3_START] = 0x09CC,
+ [PMIF_PMIC_ALL_RGN_3_END] = 0x09D0,
+ [PMIF_PMIC_ALL_RGN_31_START] = 0x0D34,
+ [PMIF_PMIC_ALL_RGN_31_END] = 0x0D38,
+ [PMIF_PMIC_ALL_INVLD_SLVID] = 0x0AAC,
+ [PMIF_PMIC_ALL_RGN_0_PER0] = 0x0AB0,
+ [PMIF_PMIC_ALL_RGN_0_PER1] = 0x0AB4,
+ [PMIF_PMIC_ALL_RGN_1_PER0] = 0x0AB8,
+ [PMIF_PMIC_ALL_RGN_2_PER0] = 0x0AC0,
+ [PMIF_PMIC_ALL_RGN_3_PER0] = 0x0AC8,
+ [PMIF_PMIC_ALL_RGN_31_PER0] = 0x0DB4,
+ [PMIF_PMIC_ALL_RGN_31_PER1] = 0x0DB8,
+ [PMIF_PMIC_ALL_RGN_OTHERS_PER0] = 0x0BA8,
+ [PMIF_PMIC_ALL_RGN_OTHERS_PER1] = 0x0BAC,
+};
+
+struct pmif pmif_spmi_arb[] = {
+ {
+ .base = (void *)PMIF_SPMI_M_BASE,
+ .regs = mt6xxx_regs,
+ .spmimst_base = (void *)SPMI_MST_M_BASE,
+ .spmimst_regs = mt6xxx_spmi_regs,
+ .mstid = SPMI_MASTER_0,
+ .read_cmd = pmif_spmi_read_cmd,
+ .write_cmd = pmif_spmi_write_cmd,
+ }, {
+ .base = (void *)PMIF_SPMI_M_BASE,
+ .regs = mt6xxx_regs,
+ .spmimst_base = (void *)SPMI_MST_M_BASE,
+ .spmimst_regs = mt6xxx_spmi_regs,
+ .mstid = SPMI_MASTER_1,
+ .read_cmd = pmif_spmi_read_cmd,
+ .write_cmd = pmif_spmi_write_cmd,
+ }, {
+ .base = (void *)PMIF_SPMI_P_BASE,
+ .regs = mt6xxx_regs,
+ .spmimst_base = (void *)SPMI_MST_P_BASE,
+ .spmimst_regs = mt6xxx_spmi_regs,
+ .mstid = SPMI_MASTER_P_1,
+ .read_cmd = pmif_spmi_read_cmd,
+ .write_cmd = pmif_spmi_write_cmd,
+ },
+};
+
+static struct spmi_device spmi_dev[] = {
+ {
+ .slvid = SPMI_SLAVE_4,
+ .grpiden = 0x1 << SPMI_GROUP_ID,
+ .mstid = SPMI_MASTER_1,
+ .hwcid_addr = 0x09,
+ .hwcid_val = 0x63,
+ .swcid_addr = 0x0B,
+ .swcid_val = 0x63,
+ .wpk_key_addr = 0x3A7,
+ .wpk_key_val = 0x9C,
+ .wpk_key_h_val = 0x9C,
+ .tma_key_addr = 0x39E,
+ .tma_key_val = 0x9C,
+ .tma_key_h_val = 0x9C,
+ .pmif_arb = &pmif_spmi_arb[SPMI_MASTER_1],
+ }, {
+ .slvid = SPMI_SLAVE_9,
+ .grpiden = 0x1 << SPMI_GROUP_ID,
+ .mstid = SPMI_MASTER_1,
+ .hwcid_addr = 0x09,
+ .hwcid_val = 0x85,
+ .swcid_addr = 0x0B,
+ .swcid_val = 0x85,
+ .wpk_key_addr = 0x3AA,
+ .wpk_key_val = 0x30,
+ .wpk_key_h_val = 0x63,
+ .tma_key_addr = 0x39E,
+ .tma_key_val = 0x7A,
+ .tma_key_h_val = 0x99,
+ .pmif_arb = &pmif_spmi_arb[SPMI_MASTER_1],
+ }, {
+ .slvid = SPMI_SLAVE_5,
+ .grpiden = 0x800,
+ .mstid = SPMI_MASTER_1,/* spmi-m */
+ .hwcid_addr = 0x09,
+ .hwcid_val = 0x73,
+ .swcid_addr = 0x0B,
+ .swcid_val = 0x73,
+ .wpk_key_addr = 0x3A7,
+ .wpk_key_val = 0x8C,
+ .wpk_key_h_val = 0x9C,
+ .tma_key_addr = 0x39E,
+ .tma_key_val = 0x8C,
+ .tma_key_h_val = 0x9C,
+ .pmif_arb = &pmif_spmi_arb[SPMI_MASTER_1],
+ }, {
+ .slvid = SPMI_SLAVE_14, /* MT6379 */
+ .grpiden = 0x800,
+ .mstid = SPMI_MASTER_1,/* spmi-m */
+ .hwcid_addr = 0x00,
+ .hwcid_val = 0x70,
+ .hwcid_mask = 0xF0,
+ .pmif_arb = &pmif_spmi_arb[SPMI_MASTER_1],
+ }, {
+ .slvid = SPMI_SLAVE_6, /* MT6316 */
+ .grpiden = 0x800,
+ .mstid = SPMI_MASTER_P_1,/* spmi-m */
+ .hwcid_addr = 0x209,
+ .hwcid_val = 0x16,
+ .swcid_addr = 0x20B,
+ .swcid_val = 0x16,
+ .wpk_key_addr = 0x3B1,
+ .wpk_key_val = 0xE9,
+ .wpk_key_h_val = 0xE6,
+ .tma_key_addr = 0x3A8,
+ .tma_key_val = 0xE9,
+ .tma_key_h_val = 0xE6,
+ .pmif_arb = &pmif_spmi_arb[SPMI_MASTER_P_1],
+ }, {
+ .slvid = SPMI_SLAVE_7,
+ .grpiden = 0x800,
+ .mstid = SPMI_MASTER_P_1,/* spmi-m */
+ .hwcid_addr = 0x209,
+ .hwcid_val = 0x16,
+ .swcid_addr = 0x20B,
+ .swcid_val = 0x16,
+ .wpk_key_addr = 0x3B1,
+ .wpk_key_val = 0xE9,
+ .wpk_key_h_val = 0xE6,
+ .tma_key_addr = 0x3A8,
+ .tma_key_val = 0xE9,
+ .tma_key_h_val = 0xE6,
+ .pmif_arb = &pmif_spmi_arb[SPMI_MASTER_P_1],
+ }, {
+ .slvid = SPMI_SLAVE_8,
+ .grpiden = 0x800,
+ .mstid = SPMI_MASTER_P_1,/* spmi-m */
+ .hwcid_addr = 0x209,
+ .hwcid_val = 0x16,
+ .swcid_addr = 0x20B,
+ .swcid_val = 0x16,
+ .wpk_key_addr = 0x3B1,
+ .wpk_key_val = 0xE9,
+ .wpk_key_h_val = 0xE6,
+ .tma_key_addr = 0x3A8,
+ .tma_key_val = 0xE9,
+ .tma_key_h_val = 0xE6,
+ .pmif_arb = &pmif_spmi_arb[SPMI_MASTER_P_1],
+ }, {
+ .slvid = SPMI_SLAVE_15,
+ .grpiden = 0x800,
+ .mstid = SPMI_MASTER_P_1,/* spmi-m */
+ .hwcid_addr = 0x209,
+ .hwcid_val = 0x16,
+ .swcid_addr = 0x20B,
+ .swcid_val = 0x16,
+ .wpk_key_addr = 0x3B1,
+ .wpk_key_val = 0xE9,
+ .wpk_key_h_val = 0xE6,
+ .tma_key_addr = 0x3A8,
+ .tma_key_val = 0xE9,
+ .tma_key_h_val = 0xE6,
+ .pmif_arb = &pmif_spmi_arb[SPMI_MASTER_P_1],
+ },
+};
+
+#if SPMI_DEBUG
+static void spmi_read_check(struct spmi_device *dev)
+{
+ uint8_t rdata = 0;
+
+ spmi_ext_register_readl(dev, dev->hwcid_addr, &rdata, 1);
+
+ if (dev->hwcid_mask) {
+ if ((rdata & dev->hwcid_mask) == (dev->hwcid_val & dev->hwcid_mask))
+ SPMI_INFO("%s pass, slvid:%d rdata = 0x%x\n", __func__,
+ dev->slvid, rdata);
+ else
+ SPMI_ERR("%s fail, slvid:%d rdata = 0x%x\n", __func__,
+ dev->slvid, rdata);
+ } else {
+ if (rdata == dev->hwcid_val)
+ SPMI_INFO("%s pass, slvid:%d rdata = 0x%x\n", __func__,
+ dev->slvid, rdata);
+ else
+ SPMI_ERR("%s fail, slvid:%d rdata = 0x%x\n", __func__,
+ dev->slvid, rdata);
+ }
+}
+
+void spmi_test(void)
+{
+ for (int k = 0; k < ARRAY_SIZE(spmi_dev); k++)
+ spmi_read_check(&spmi_dev[k]);
+}
+#endif
+
+int platform_pmif_spmi_init(void)
+{
+ spmi_device_register(spmi_dev, ARRAY_SIZE(spmi_dev));
+
+#if SPMI_DEBUG
+ spmi_test();
+#endif
+ return 0;
+}
+MTK_ARCH_INIT(platform_pmif_spmi_init);
diff --git a/plat/mediatek/drivers/spmi/pmif_common.c b/plat/mediatek/drivers/spmi/pmif_common.c
new file mode 100644
index 0000000..5e45f0b
--- /dev/null
+++ b/plat/mediatek/drivers/spmi/pmif_common.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2025, MediaTek Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <lib/mmio.h>
+#include <lib/spinlock.h>
+
+#include <pmif.h>
+#include "pmif_common.h"
+#include "spmi_common.h"
+#include "spmi_sw.h"
+
+#define PMIF_CMD_REG_0 0
+#define PMIF_CMD_REG 1
+#define PMIF_CMD_EXT_REG 2
+#define PMIF_CMD_EXT_REG_LONG 3
+#define PMIF_READ_CMD_MIN 0x60
+#define PMIF_READ_CMD_MAX 0x7F
+#define PMIF_READ_CMD_EXT_MIN 0x20
+#define PMIF_READ_CMD_EXT_MAX 0x2F
+#define PMIF_READ_CMD_EXT_LONG_MIN 0x38
+#define PMIF_READ_CMD_EXT_LONG_MAX 0x3F
+#define PMIF_WRITE_CMD_MIN 0x40
+#define PMIF_WRITE_CMD_MAX 0x5F
+#define PMIF_WRITE_CMD_EXT_MAX 0xF
+#define PMIF_WRITE_CMD_EXT_LONG_MIN 0x30
+#define PMIF_WRITE_CMD_EXT_LONG_MAX 0x37
+#define PMIF_WRITE_CMD_0_MIN 0x80
+
+/* macro for SWINF_FSM */
+#define SWINF_FSM_IDLE 0x00
+#define SWINF_FSM_REQ 0x02
+#define SWINF_FSM_WFDLE 0x04
+#define SWINF_FSM_WFVLDCLR 0x06
+
+#define GET_SWINF_FSM(x) (((x) >> 1) & 0x7)
+#define GET_PMIF_INIT_DONE(x) (((x) >> 15) & 0x1)
+#define TIMEOUT_WAIT_IDLE_US 10000 /* 10ms */
+
+#define PMIF_RW_CMD_SET(opc, rw, sid, bc, addr) \
+ (((opc) << 30) | ((rw) << 29) | ((sid) << 24) | ((bc) << 16) | (addr))
+
+static spinlock_t pmif_lock;
+
+struct pmif *get_pmif_controller(int inf, int mstid)
+{
+ return &pmif_spmi_arb[mstid];
+}
+
+static int pmif_check_idle(int mstid)
+{
+ struct pmif *arb = get_pmif_controller(PMIF_SPMI, mstid);
+ unsigned int reg_rdata, offset = 0;
+
+ do {
+ offset = arb->regs[PMIF_SWINF_3_STA];
+ reg_rdata = mmio_read_32((uintptr_t)(arb->base + offset));
+ } while (GET_SWINF_FSM(reg_rdata) != SWINF_FSM_IDLE);
+
+ return 0;
+}
+
+static int pmif_check_vldclr(int mstid)
+{
+ struct pmif *arb = get_pmif_controller(PMIF_SPMI, mstid);
+ unsigned int reg_rdata, offset = 0;
+
+ do {
+ offset = arb->regs[PMIF_SWINF_3_STA];
+ reg_rdata = mmio_read_32((uintptr_t)(arb->base + offset));
+ } while (GET_SWINF_FSM(reg_rdata) != SWINF_FSM_WFVLDCLR);
+
+ return 0;
+}
+
+int pmif_spmi_read_cmd(struct pmif *arb, uint8_t opc, uint8_t sid,
+ uint16_t addr, uint8_t *buf, uint8_t len)
+{
+ int ret;
+ uint32_t offset = 0, data = 0;
+ uint8_t bc = len - 1;
+
+ if (sid > SPMI_MAX_SLAVE_ID || len > PMIF_BYTECNT_MAX)
+ return -EINVAL;
+
+ /* Check the opcode */
+ if (opc >= PMIF_READ_CMD_MIN && opc <= PMIF_READ_CMD_MAX)
+ opc = PMIF_CMD_REG;
+ else if (opc >= PMIF_READ_CMD_EXT_MIN && opc <= PMIF_READ_CMD_EXT_MAX)
+ opc = PMIF_CMD_EXT_REG;
+ else if (opc >= PMIF_READ_CMD_EXT_LONG_MIN && opc <= PMIF_READ_CMD_EXT_LONG_MAX)
+ opc = PMIF_CMD_EXT_REG_LONG;
+ else
+ return -EINVAL;
+
+ spin_lock(&pmif_lock);
+
+ /* Wait for Software Interface FSM state to be IDLE. */
+ ret = pmif_check_idle(arb->mstid);
+ if (ret)
+ goto done;
+
+ /* Send the command. */
+ offset = arb->regs[PMIF_SWINF_3_ACC];
+ mmio_write_32((uintptr_t)(arb->base + offset), PMIF_RW_CMD_SET(opc, 0, sid, bc, addr));
+ /*
+ * Wait for Software Interface FSM state to be WFVLDCLR,
+ * read the data and clear the valid flag.
+ */
+ ret = pmif_check_vldclr(arb->mstid);
+ if (ret)
+ goto done;
+
+ offset = arb->regs[PMIF_SWINF_3_RDATA_31_0];
+
+ data = mmio_read_32((uintptr_t)(arb->base + offset));
+ memcpy(buf, &data, (bc & 3) + 1);
+
+ offset = arb->regs[PMIF_SWINF_3_VLD_CLR];
+ mmio_write_32((uintptr_t)(arb->base + offset), 0x1);
+
+done:
+ spin_unlock(&pmif_lock);
+ return ret;
+}
+
+int pmif_spmi_write_cmd(struct pmif *arb, uint8_t opc, uint8_t sid, uint16_t addr,
+ const uint8_t *buf, uint8_t len)
+{
+ int ret;
+ uint32_t offset = 0, data = 0;
+ uint8_t bc = len - 1;
+
+ if (sid > SPMI_MAX_SLAVE_ID || len > PMIF_BYTECNT_MAX)
+ return -EINVAL;
+
+ /* Check the opcode */
+ if (opc >= PMIF_WRITE_CMD_MIN && opc <= PMIF_WRITE_CMD_MAX)
+ opc = PMIF_CMD_REG;
+ else if (opc <= PMIF_WRITE_CMD_EXT_MAX)
+ opc = PMIF_CMD_EXT_REG;
+ else if (opc >= PMIF_WRITE_CMD_EXT_LONG_MIN && opc <= PMIF_WRITE_CMD_EXT_LONG_MAX)
+ opc = PMIF_CMD_EXT_REG_LONG;
+ else if (opc >= PMIF_WRITE_CMD_0_MIN)
+ opc = PMIF_CMD_REG_0;
+ else
+ return -EINVAL;
+
+ spin_lock(&pmif_lock);
+
+ /* Wait for Software Interface FSM state to be IDLE. */
+ ret = pmif_check_idle(arb->mstid);
+ if (ret)
+ goto done;
+
+ /* Set the write data. */
+ offset = arb->regs[PMIF_SWINF_3_WDATA_31_0];
+ memcpy(&data, buf, (bc & 3) + 1);
+ mmio_write_32((uintptr_t)(arb->base + offset), data);
+ /* Send the command. */
+ offset = arb->regs[PMIF_SWINF_3_ACC];
+ mmio_write_32((uintptr_t)(arb->base + offset), PMIF_RW_CMD_SET(opc, 1, sid, bc, addr));
+
+done:
+ spin_unlock(&pmif_lock);
+ return ret;
+}
diff --git a/plat/mediatek/drivers/spmi/pmif_common.h b/plat/mediatek/drivers/spmi/pmif_common.h
new file mode 100644
index 0000000..bdb2722
--- /dev/null
+++ b/plat/mediatek/drivers/spmi/pmif_common.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2025, MediaTek Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PMIF_COMMON_H
+#define PMIF_COMMON_H
+
+#include <stdint.h>
+
+enum {
+ PMIF_CMD_REG_0,
+ PMIF_CMD_REG,
+ PMIF_CMD_EXT_REG,
+ PMIF_CMD_EXT_REG_LONG,
+};
+
+struct pmif {
+ void *base;
+ uint16_t *regs;
+ void *spmimst_base;
+ uint16_t *spmimst_regs;
+ uint32_t mstid;
+ int (*read_cmd)(struct pmif *arb, uint8_t opc, uint8_t sid, uint16_t addr, uint8_t *buf,
+ uint8_t len);
+ int (*write_cmd)(struct pmif *arb, uint8_t opc, uint8_t sid, uint16_t addr,
+ const uint8_t *buf, uint8_t len);
+};
+
+enum {
+ PMIF_SPMI,
+ PMIF_SPI
+};
+
+int pmif_spmi_read_cmd(struct pmif *arb, uint8_t opc, uint8_t sid, uint16_t addr, uint8_t *buf,
+ uint8_t len);
+int pmif_spmi_write_cmd(struct pmif *arb, uint8_t opc, uint8_t sid, uint16_t addr,
+ const uint8_t *buf, uint8_t len);
+struct pmif *get_pmif_controller(int inf, int mstid);
+extern struct pmif pmif_spmi_arb[];
+#endif
diff --git a/plat/mediatek/drivers/spmi/pmif_v1/pmif.h b/plat/mediatek/drivers/spmi/pmif_v1/pmif.h
new file mode 100644
index 0000000..b136262
--- /dev/null
+++ b/plat/mediatek/drivers/spmi/pmif_v1/pmif.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2025, Mediatek Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PMIF_H
+#define PMIF_H
+
+#include <stdint.h>
+
+#include <platform_def.h>
+
+#include <drivers/spmi/pmif_common.h>
+#include <drivers/spmi/spmi_common.h>
+
+enum pmif_regs {
+ PMIF_INIT_DONE,
+ PMIF_INF_EN,
+ PMIF_ARB_EN,
+ PMIF_IRQ_EVENT_EN_0,
+ PMIF_IRQ_FLAG_0,
+ PMIF_IRQ_CLR_0,
+ PMIF_IRQ_EVENT_EN_2,
+ PMIF_IRQ_FLAG_2,
+ PMIF_IRQ_CLR_2,
+ PMIF_WDT_CTRL,
+ PMIF_WDT_EVENT_EN_1,
+ PMIF_WDT_FLAG_1,
+ PMIF_SWINF_2_ACC,
+ PMIF_SWINF_2_WDATA_31_0,
+ PMIF_SWINF_2_WDATA_63_32,
+ PMIF_SWINF_2_RDATA_31_0,
+ PMIF_SWINF_2_RDATA_63_32,
+ PMIF_SWINF_2_VLD_CLR,
+ PMIF_SWINF_2_STA,
+ PMIF_SWINF_3_ACC,
+ PMIF_SWINF_3_WDATA_31_0,
+ PMIF_SWINF_3_WDATA_63_32,
+ PMIF_SWINF_3_RDATA_31_0,
+ PMIF_SWINF_3_RDATA_63_32,
+ PMIF_SWINF_3_VLD_CLR,
+ PMIF_SWINF_3_STA,
+ /* HW MPU */
+ PMIF_PMIC_ALL_RGN_EN_1,
+ PMIF_PMIC_ALL_RGN_EN_2,
+ PMIF_PMIC_ALL_RGN_0_START,
+ PMIF_PMIC_ALL_RGN_0_END,
+ PMIF_PMIC_ALL_RGN_1_START,
+ PMIF_PMIC_ALL_RGN_1_END,
+ PMIF_PMIC_ALL_RGN_2_START,
+ PMIF_PMIC_ALL_RGN_2_END,
+ PMIF_PMIC_ALL_RGN_3_START,
+ PMIF_PMIC_ALL_RGN_3_END,
+ PMIF_PMIC_ALL_RGN_31_START,
+ PMIF_PMIC_ALL_RGN_31_END,
+ PMIF_PMIC_ALL_INVLD_SLVID,
+ PMIF_PMIC_ALL_RGN_0_PER0,
+ PMIF_PMIC_ALL_RGN_0_PER1,
+ PMIF_PMIC_ALL_RGN_1_PER0,
+ PMIF_PMIC_ALL_RGN_2_PER0,
+ PMIF_PMIC_ALL_RGN_3_PER0,
+ PMIF_PMIC_ALL_RGN_31_PER0,
+ PMIF_PMIC_ALL_RGN_31_PER1,
+ PMIF_PMIC_ALL_RGN_OTHERS_PER0,
+ PMIF_PMIC_ALL_RGN_OTHERS_PER1,
+};
+#endif
diff --git a/plat/mediatek/drivers/spmi/rules.mk b/plat/mediatek/drivers/spmi/rules.mk
new file mode 100644
index 0000000..ce6f465
--- /dev/null
+++ b/plat/mediatek/drivers/spmi/rules.mk
@@ -0,0 +1,19 @@
+#
+# Copyright (c) 2025, MediaTek Inc. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+LOCAL_DIR := $(call GET_LOCAL_DIR)
+
+MODULE := spmi
+
+PLAT_INCLUDES += -I${MTK_PLAT}/drivers/spmi/pmif_v1
+
+#Add your source code here
+LOCAL_SRCS-y := ${LOCAL_DIR}/pmif_common.c
+LOCAL_SRCS-y += ${LOCAL_DIR}/spmi_common.c
+LOCAL_SRCS-y += ${LOCAL_DIR}/${MTK_SOC}/platform_pmif_spmi.c
+
+#Epilogue, build as module
+$(eval $(call MAKE_MODULE,$(MODULE),$(LOCAL_SRCS-y),$(MTK_BL)))
diff --git a/plat/mediatek/drivers/spmi/spmi_common.c b/plat/mediatek/drivers/spmi/spmi_common.c
new file mode 100644
index 0000000..c9b05ec
--- /dev/null
+++ b/plat/mediatek/drivers/spmi/spmi_common.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2025, MediaTek Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <errno.h>
+
+#include <lib/mmio.h>
+#include <platform_def.h>
+
+#include "spmi_common.h"
+#include "spmi_sw.h"
+
+/* SPMI Commands */
+#define SPMI_CMD_EXT_WRITE 0x00
+#define SPMI_CMD_EXT_READ 0x20
+#define SPMI_CMD_EXT_WRITEL 0x30
+#define SPMI_CMD_EXT_READL 0x38
+#define SPMI_CMD_WRITE 0x40
+#define SPMI_CMD_READ 0x60
+#define SPMI_CMD_ZERO_WRITE 0x80
+#define SPMI_READ_ADDR_MAX 0x1F
+
+static struct spmi_device *spmi_dev[SPMI_MAX_SLAVE_ID];
+
+int spmi_register_zero_write(struct spmi_device *dev, uint8_t data)
+{
+ return dev->pmif_arb->write_cmd(dev->pmif_arb, SPMI_CMD_ZERO_WRITE,
+ dev->slvid, 0, &data, 1);
+}
+
+int spmi_register_read(struct spmi_device *dev, uint8_t addr, uint8_t *buf)
+{
+ /* 5-bit register address */
+ if (addr > SPMI_READ_ADDR_MAX)
+ return -EINVAL;
+
+ return dev->pmif_arb->read_cmd(dev->pmif_arb, SPMI_CMD_READ, dev->slvid, addr, buf, 1);
+}
+
+int spmi_register_write(struct spmi_device *dev, uint8_t addr, uint8_t data)
+{
+ /* 5-bit register address */
+ if (addr > SPMI_READ_ADDR_MAX)
+ return -EINVAL;
+
+ return dev->pmif_arb->write_cmd(dev->pmif_arb, SPMI_CMD_WRITE,
+ dev->slvid, addr, &data, 1);
+}
+
+int spmi_ext_register_read(struct spmi_device *dev, uint8_t addr, uint8_t *buf,
+ uint8_t len)
+{
+ /* 8-bit register address, up to 16 bytes */
+ if (len == 0 || len > 16)
+ return -EINVAL;
+
+ return dev->pmif_arb->read_cmd(dev->pmif_arb, SPMI_CMD_EXT_READ,
+ dev->slvid, addr, buf, len);
+}
+
+int spmi_ext_register_write(struct spmi_device *dev, uint8_t addr,
+ const uint8_t *buf, uint8_t len)
+{
+ /* 8-bit register address, up to 16 bytes */
+ if (len == 0 || len > 16)
+ return -EINVAL;
+
+ return dev->pmif_arb->write_cmd(dev->pmif_arb, SPMI_CMD_EXT_WRITE,
+ dev->slvid, addr, buf, len);
+}
+
+int spmi_ext_register_readl(struct spmi_device *dev, uint16_t addr,
+ uint8_t *buf, uint8_t len)
+{
+ /* 8-bit register address, up to 16 bytes */
+ if (len == 0 || len > 16)
+ return -EINVAL;
+
+ return dev->pmif_arb->read_cmd(dev->pmif_arb, SPMI_CMD_EXT_READL,
+ dev->slvid, addr, buf, len);
+}
+
+int spmi_ext_register_writel(struct spmi_device *dev, uint16_t addr,
+ const uint8_t *buf, uint8_t len)
+{
+ /* 8-bit register address, up to 16 bytes */
+ if (len == 0 || len > 16)
+ return -EINVAL;
+
+ return dev->pmif_arb->write_cmd(dev->pmif_arb, SPMI_CMD_EXT_WRITEL,
+ dev->slvid, addr, buf, len);
+}
+
+int spmi_ext_register_readl_field(struct spmi_device *dev, uint16_t addr,
+ uint8_t *buf, uint16_t mask, uint16_t shift)
+{
+ int ret;
+ uint8_t rdata = 0;
+
+ ret = dev->pmif_arb->read_cmd(dev->pmif_arb, SPMI_CMD_EXT_READL,
+ dev->slvid, addr, &rdata, 1);
+ if (!ret)
+ *buf = (rdata >> shift) & mask;
+
+ return ret;
+}
+
+int spmi_ext_register_writel_field(struct spmi_device *dev, uint16_t addr,
+ uint8_t data, uint16_t mask, uint16_t shift)
+{
+ int ret;
+ uint8_t tmp = 0;
+
+ ret = spmi_ext_register_readl(dev, addr, &tmp, 1);
+ if (ret)
+ return ret;
+
+ tmp &= ~(mask << shift);
+ tmp |= (data << shift);
+ return dev->pmif_arb->write_cmd(dev->pmif_arb, SPMI_CMD_EXT_WRITEL,
+ dev->slvid, addr, &tmp, 1);
+}
+
+struct spmi_device *get_spmi_device(int mstid, int slvid)
+{
+ if (slvid >= SPMI_MAX_SLAVE_ID || slvid < 0) {
+ SPMI_ERR("failed to get spmi_device with slave id %d\n", slvid);
+ return NULL;
+ }
+ return spmi_dev[slvid];
+}
+
+int spmi_device_register(struct spmi_device *platform_spmi_dev, unsigned int num_devs)
+{
+ int i;
+
+ if (!platform_spmi_dev || num_devs == 0)
+ return -EINVAL;
+
+ for (i = 0; i < num_devs; i++) {
+ if (platform_spmi_dev[i].slvid >= SPMI_MAX_SLAVE_ID ||
+ platform_spmi_dev[i].slvid < 0) {
+ SPMI_INFO("invalid slave id %d\n", platform_spmi_dev[i].slvid);
+ continue;
+ }
+ if (!spmi_dev[platform_spmi_dev[i].slvid])
+ spmi_dev[platform_spmi_dev[i].slvid] = &platform_spmi_dev[i];
+ else {
+ SPMI_INFO("duplicated slave id %d\n", platform_spmi_dev[i].slvid);
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static int spmi_ctrl_op_st(int mstid, unsigned int grpiden, unsigned int sid,
+ unsigned int cmd)
+{
+ struct pmif *arb = get_pmif_controller(PMIF_SPMI, mstid);
+ unsigned int rdata = 0x0;
+ uintptr_t spmi_grp_id_en_addr =
+ (uintptr_t)(arb->spmimst_base + arb->spmimst_regs[SPMI_GRP_ID_EN]);
+ uintptr_t spmi_op_st_ctrl_addr =
+ (uintptr_t)(arb->spmimst_base + arb->spmimst_regs[SPMI_OP_ST_CTRL]);
+ uintptr_t spmi_op_st_sta_addr =
+ (uintptr_t)(arb->spmimst_base + arb->spmimst_regs[SPMI_OP_ST_STA]);
+
+ /* gid is 0x800 */
+ mmio_write_32(spmi_grp_id_en_addr, grpiden);
+
+ if (grpiden == (1 << SPMI_GROUP_ID))
+ mmio_write_32(spmi_op_st_ctrl_addr, (cmd << 0x4) | SPMI_GROUP_ID);
+ else
+ mmio_write_32(spmi_op_st_ctrl_addr, (cmd << 0x4) | sid);
+
+ SPMI_INFO("%s 0x%x\n", __func__, mmio_read_32(spmi_op_st_ctrl_addr));
+
+ do {
+ rdata = mmio_read_32(spmi_op_st_sta_addr);
+ SPMI_INFO("%s 0x%x\n", __func__, rdata);
+
+ if (((rdata >> 0x1) & SPMI_OP_ST_NACK) == SPMI_OP_ST_NACK) {
+ SPMI_ERR("SPMI_OP_ST_NACK occurs! OP_ST_STA = 0x%x\n", rdata);
+ break;
+ }
+ } while ((rdata & SPMI_OP_ST_BUSY) == SPMI_OP_ST_BUSY);
+
+ return 0;
+}
+
+int spmi_command_shutdown(int mstid, struct spmi_device *dev, unsigned int grpiden)
+{
+ if (grpiden != (1 << SPMI_GROUP_ID))
+ dev->slvid = grpiden;
+
+ return spmi_ctrl_op_st(mstid, grpiden, dev->slvid, SPMI_SHUTDOWN);
+}
diff --git a/plat/mediatek/drivers/spmi/spmi_common.h b/plat/mediatek/drivers/spmi/spmi_common.h
new file mode 100644
index 0000000..16850b1
--- /dev/null
+++ b/plat/mediatek/drivers/spmi/spmi_common.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2025, Mediatek Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SPMI_COMMON_H
+#define SPMI_COMMON_H
+
+#include <stdint.h>
+
+#include <platform_def.h>
+#include "pmif_common.h"
+
+/* Read/write byte limitation */
+#define PMIF_BYTECNT_MAX 2
+
+#define SPMI_GROUP_ID 0xB
+
+/* enum marco for cmd/channel */
+enum spmi_master {
+ SPMI_MASTER_0 = 0,
+ SPMI_MASTER_1,
+ SPMI_MASTER_P_1,
+ SPMI_MASTER_MAX
+};
+
+enum spmi_slave {
+ SPMI_SLAVE_0 = 0,
+ SPMI_SLAVE_1,
+ SPMI_SLAVE_2,
+ SPMI_SLAVE_3,
+ SPMI_SLAVE_4,
+ SPMI_SLAVE_5,
+ SPMI_SLAVE_6,
+ SPMI_SLAVE_7,
+ SPMI_SLAVE_8,
+ SPMI_SLAVE_9,
+ SPMI_SLAVE_10,
+ SPMI_SLAVE_11,
+ SPMI_SLAVE_12,
+ SPMI_SLAVE_13,
+ SPMI_SLAVE_14,
+ SPMI_SLAVE_15,
+ SPMI_MAX_SLAVE_ID
+};
+
+enum slv_type {
+ BUCK_CPU,
+ BUCK_GPU,
+ BUCK_MD,
+ BUCK_RF,
+ MAIN_PMIC,
+ BUCK_VPU,
+ SUB_PMIC,
+ CLOCK_PMIC,
+ SECOND_PMIC,
+ SLV_TYPE_MAX
+};
+
+enum slv_type_id {
+ BUCK_RF_ID = 1,
+ BUCK_MD_ID = 3,
+ MAIN_PMIC_ID = 5,
+ BUCK_CPU_ID = 6,
+ BUCK_GPU_ID = 7,
+ BUCK_VPU_ID,
+ SUB_PMIC_ID = 10,
+ CLOCK_PMIC_ID = 11,
+ SECOND_PMIC_ID = 12,
+ SLV_TYPE_ID_MAX
+};
+
+enum {
+ SPMI_OP_ST_BUSY = 1,
+ SPMI_OP_ST_ACK = 0,
+ SPMI_OP_ST_NACK = 1
+};
+
+struct spmi_device {
+ int slvid;
+ int grpiden;
+ enum slv_type type;
+ enum slv_type_id type_id;
+ int mstid;
+ uint16_t hwcid_addr;
+ uint8_t hwcid_val;
+ uint16_t hwcid_mask;
+ uint16_t swcid_addr;
+ uint8_t swcid_val;
+ uint16_t wpk_key_addr;
+ uint16_t wpk_key_val;
+ uint16_t wpk_key_h_val;
+ uint16_t tma_key_addr;
+ uint16_t tma_key_val;
+ uint16_t tma_key_h_val;
+ uint16_t rcs_en_addr;
+ uint16_t rcs_slvid_addr;
+ struct pmif *pmif_arb;
+};
+
+int spmi_command_shutdown(int mstid, struct spmi_device *dev, unsigned int grpiden);
+#endif
diff --git a/plat/mediatek/drivers/spmi/spmi_sw.h b/plat/mediatek/drivers/spmi/spmi_sw.h
new file mode 100644
index 0000000..fae2704
--- /dev/null
+++ b/plat/mediatek/drivers/spmi/spmi_sw.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2025, Mediatek Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SPMI_SW_H
+#define SPMI_SW_H
+
+#include <common/debug.h>
+#include <drivers/delay_timer.h>
+#include <mt_timer.h>
+
+enum spmi_regs {
+ SPMI_OP_ST_CTRL,
+ SPMI_GRP_ID_EN,
+ SPMI_OP_ST_STA,
+ SPMI_MST_SAMPL,
+ SPMI_MST_REQ_EN,
+ /* RCS support */
+ SPMI_RCS_CTRL,
+ SPMI_SLV_3_0_EINT,
+ SPMI_SLV_7_4_EINT,
+ SPMI_SLV_B_8_EINT,
+ SPMI_SLV_F_C_EINT,
+ SPMI_REC_CTRL,
+ SPMI_REC0,
+ SPMI_REC1,
+ SPMI_REC2,
+ SPMI_REC3,
+ SPMI_REC4,
+ SPMI_REC_CMD_DEC,
+ SPMI_DEC_DBG,
+ SPMI_MST_DBG
+};
+
+/* DEBUG MARCO */
+#define SPMITAG "[SPMI] "
+#define SPMI_ERR(fmt, arg...) ERROR(SPMITAG fmt, ##arg)
+#define SPMI_ERRL(fmt, arg...) ERROR(fmt, ##arg)
+#define SPMI_INFO(fmt, arg...) INFO(SPMITAG fmt, ##arg)
+
+#define wait_us(cond, timeout) \
+({ \
+ uint64_t __now, __end, __ret; \
+ \
+ __end = sched_clock() + timeout; \
+ for (;;) { \
+ if (cond) { \
+ __ret = timeout; \
+ break; \
+ } \
+ __now = sched_clock(); \
+ if (__end <= __now) { \
+ __ret = 0; \
+ break; \
+ } \
+ } \
+ __ret; \
+})
+
+enum {
+ SPMI_RESET = 0,
+ SPMI_SLEEP,
+ SPMI_SHUTDOWN,
+ SPMI_WAKEUP
+};
+
+#endif
diff --git a/plat/mediatek/include/drivers/spmi_api.h b/plat/mediatek/include/drivers/spmi_api.h
new file mode 100644
index 0000000..25b16cf
--- /dev/null
+++ b/plat/mediatek/include/drivers/spmi_api.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2025, MediaTek Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SPMI_API_H
+#define SPMI_API_H
+
+#include <stdint.h>
+
+#include <drivers/spmi/spmi_common.h>
+
+/* external API */
+int spmi_register_zero_write(struct spmi_device *dev, uint8_t data);
+int spmi_register_read(struct spmi_device *dev, uint8_t addr, uint8_t *buf);
+int spmi_register_write(struct spmi_device *dev, uint8_t addr, uint8_t data);
+int spmi_ext_register_read(struct spmi_device *dev, uint8_t addr, uint8_t *buf,
+ uint8_t len);
+int spmi_ext_register_write(struct spmi_device *dev, uint8_t addr,
+ const uint8_t *buf, uint8_t len);
+int spmi_ext_register_readl(struct spmi_device *dev, uint16_t addr,
+ uint8_t *buf, uint8_t len);
+int spmi_ext_register_writel(struct spmi_device *dev, uint16_t addr,
+ const uint8_t *buf, uint8_t len);
+int spmi_ext_register_readl_field(struct spmi_device *dev, uint16_t addr,
+ uint8_t *buf, uint16_t mask, uint16_t shift);
+int spmi_ext_register_writel_field(struct spmi_device *dev, uint16_t addr,
+ uint8_t data, uint16_t mask, uint16_t shift);
+struct spmi_device *get_spmi_device(int mstid, int slvid);
+int spmi_device_register(struct spmi_device *platform_spmi_dev, int num_devs);
+
+#endif
diff --git a/plat/mediatek/mt8196/include/platform_def.h b/plat/mediatek/mt8196/include/platform_def.h
index 6b6416a..a19fad7 100644
--- a/plat/mediatek/mt8196/include/platform_def.h
+++ b/plat/mediatek/mt8196/include/platform_def.h
@@ -95,6 +95,20 @@
#define UART_BAUDRATE (115200)
/*******************************************************************************
+ * PMIF address
+ ******************************************************************************/
+#define PMIF_SPMI_M_BASE (IO_PHYS + 0x0C01A000)
+#define PMIF_SPMI_P_BASE (IO_PHYS + 0x0C018000)
+#define PMIF_SPMI_SIZE 0x1000
+
+/*******************************************************************************
+ * SPMI address
+ ******************************************************************************/
+#define SPMI_MST_M_BASE (IO_PHYS + 0x0C01C000)
+#define SPMI_MST_P_BASE (IO_PHYS + 0x0C01C800)
+#define SPMI_MST_SIZE 0x1000
+
+/*******************************************************************************
* Infra IOMMU related constants
******************************************************************************/
#define INFRACFG_AO_BASE (IO_PHYS + 0x00001000)
diff --git a/plat/mediatek/mt8196/plat_config.mk b/plat/mediatek/mt8196/plat_config.mk
index a793f1b..a983de3 100644
--- a/plat/mediatek/mt8196/plat_config.mk
+++ b/plat/mediatek/mt8196/plat_config.mk
@@ -51,7 +51,7 @@
CONFIG_MTK_PMIC_LOWPOWER := y
CONFIG_MTK_PMIC_SHUTDOWN_CFG := y
CONFIG_MTK_PMIC_SPT_SUPPORT := n
-
+CONFIG_MTK_SPMI := y
PMIC_CHIP := mt6363
ENABLE_FEAT_AMU := 1
diff --git a/plat/mediatek/mt8196/platform.mk b/plat/mediatek/mt8196/platform.mk
index 7e1633e..0432e56 100644
--- a/plat/mediatek/mt8196/platform.mk
+++ b/plat/mediatek/mt8196/platform.mk
@@ -34,6 +34,7 @@
MODULES-y += $(MTK_PLAT)/helpers
MODULES-y += $(MTK_PLAT)/topology
MODULES-$(CONFIG_MTK_PMIC) += $(MTK_PLAT)/drivers/pmic
+MODULES-$(CONFIG_MTK_SPMI) += $(MTK_PLAT)/drivers/spmi
ifneq ($(MTKLIB_PATH),)
LDFLAGS += -L $(dir $(MTKLIB_PATH))