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>
This commit is contained in:
Hope Wang 2024-12-13 16:21:01 +08:00 committed by Wenzhen Yu
parent d4e6f98d7f
commit adf73ae20a
12 changed files with 1012 additions and 1 deletions

View file

@ -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);

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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)))

View file

@ -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);
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
******************************************************************************/

View file

@ -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

View file

@ -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))