From adf73ae20a7aa6f8230cb7a19551edb239db8afe Mon Sep 17 00:00:00 2001 From: Hope Wang Date: Fri, 13 Dec 2024 16:21:01 +0800 Subject: [PATCH] feat(mt8196): add SPMI driver Add SPMI and PMIF driver for PMIC communication Change-Id: Iad1d90381d6dad6b3e92fd9d6a3ce02fa11d15f1 Signed-off-by: Hope Wang --- .../drivers/spmi/mt8196/platform_pmif_spmi.c | 290 ++++++++++++++++++ plat/mediatek/drivers/spmi/pmif_common.c | 173 +++++++++++ plat/mediatek/drivers/spmi/pmif_common.h | 42 +++ plat/mediatek/drivers/spmi/pmif_v1/pmif.h | 68 ++++ plat/mediatek/drivers/spmi/rules.mk | 19 ++ plat/mediatek/drivers/spmi/spmi_common.c | 199 ++++++++++++ plat/mediatek/drivers/spmi/spmi_common.h | 103 +++++++ plat/mediatek/drivers/spmi/spmi_sw.h | 69 +++++ plat/mediatek/include/drivers/spmi_api.h | 33 ++ plat/mediatek/mt8196/include/platform_def.h | 14 + plat/mediatek/mt8196/plat_config.mk | 2 +- plat/mediatek/mt8196/platform.mk | 1 + 12 files changed, 1012 insertions(+), 1 deletion(-) create mode 100644 plat/mediatek/drivers/spmi/mt8196/platform_pmif_spmi.c create mode 100644 plat/mediatek/drivers/spmi/pmif_common.c create mode 100644 plat/mediatek/drivers/spmi/pmif_common.h create mode 100644 plat/mediatek/drivers/spmi/pmif_v1/pmif.h create mode 100644 plat/mediatek/drivers/spmi/rules.mk create mode 100644 plat/mediatek/drivers/spmi/spmi_common.c create mode 100644 plat/mediatek/drivers/spmi/spmi_common.h create mode 100644 plat/mediatek/drivers/spmi/spmi_sw.h create mode 100644 plat/mediatek/include/drivers/spmi_api.h 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 000000000..5aab85656 --- /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 + +#include +#include +#include +#include +#include +#include +#include + +#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 000000000..5e45f0b77 --- /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 +#include +#include + +#include +#include + +#include +#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 000000000..bdb27226e --- /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 + +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 000000000..b1362623b --- /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 + +#include + +#include +#include + +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 000000000..ce6f465cb --- /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 000000000..c9b05ecf6 --- /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 + +#include +#include + +#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 000000000..16850b15c --- /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 + +#include +#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 000000000..fae270418 --- /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 +#include +#include + +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 000000000..25b16cf66 --- /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 + +#include + +/* 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 6b6416ac0..a19fad734 100644 --- a/plat/mediatek/mt8196/include/platform_def.h +++ b/plat/mediatek/mt8196/include/platform_def.h @@ -94,6 +94,20 @@ #define UART0_BASE (IO_PHYS + 0x06000000) #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 ******************************************************************************/ diff --git a/plat/mediatek/mt8196/plat_config.mk b/plat/mediatek/mt8196/plat_config.mk index a793f1bee..a983de3e7 100644 --- a/plat/mediatek/mt8196/plat_config.mk +++ b/plat/mediatek/mt8196/plat_config.mk @@ -51,7 +51,7 @@ CONFIG_MTK_PMIC := y 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 7e1633e74..0432e5614 100644 --- a/plat/mediatek/mt8196/platform.mk +++ b/plat/mediatek/mt8196/platform.mk @@ -34,6 +34,7 @@ MODULES-y += $(MTK_PLAT)/drivers/vcp 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))