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/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;
+}