arm-trusted-firmware/plat/mediatek/drivers/spmi/pmif_common.c
Hope Wang adf73ae20a 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>
2025-01-22 11:51:07 +08:00

173 lines
4.4 KiB
C

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