mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-16 09:34:18 +00:00

Add SPMI and PMIF driver for PMIC communication Change-Id: Iad1d90381d6dad6b3e92fd9d6a3ce02fa11d15f1 Signed-off-by: Hope Wang <hope.wang@mediatek.corp-partner.google.com>
173 lines
4.4 KiB
C
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;
|
|
}
|