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