From c1128ae64918b2fdb50d7d29b2fff11f55c5a1b7 Mon Sep 17 00:00:00 2001 From: Kapil Porwal Date: Mon, 25 Aug 2025 09:34:13 +0530 Subject: [PATCH] soc/qualcomm/cmn: Add SPMI driver Add Qualcomm PMIC ARB driver. BUG=b:438004604 TEST=Communicate with IPMI slaves. Change-Id: Id872068ea377175a791e478b45e02aa9fcc4327d Signed-off-by: Kapil Porwal Reviewed-on: https://review.coreboot.org/c/coreboot/+/88926 Reviewed-by: Subrata Banik Tested-by: build bot (Jenkins) --- .../qualcomm/common/include/soc/qcom_spmi.h | 12 ++ src/soc/qualcomm/common/spmi.c | 127 ++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 src/soc/qualcomm/common/include/soc/qcom_spmi.h create mode 100644 src/soc/qualcomm/common/spmi.c diff --git a/src/soc/qualcomm/common/include/soc/qcom_spmi.h b/src/soc/qualcomm/common/include/soc/qcom_spmi.h new file mode 100644 index 0000000000..1c0ae6121f --- /dev/null +++ b/src/soc/qualcomm/common/include/soc/qcom_spmi.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __SOC_QCOM_SPMI_H__ +#define __SOC_QCOM_SPMI_H__ + +#include +#include + +int spmi_read8(uint32_t addr); +int spmi_write8(uint32_t addr, uint8_t data); + +#endif // __SOC_QCOM_SPMI_H__ diff --git a/src/soc/qualcomm/common/spmi.c b/src/soc/qualcomm/common/spmi.c new file mode 100644 index 0000000000..51a37058b0 --- /dev/null +++ b/src/soc/qualcomm/common/spmi.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include + +#define PPID_MASK (0xfffU << 8) + +/* These are opcodes specific to this SPMI arbitrator, *not* SPMI commands. */ +#define OPC_EXT_WRITEL 0 +#define OPC_EXT_READL 1 + +#define ARB_STATUS_DONE BIT(0) +#define ARB_STATUS_FAILURE BIT(1) +#define ARB_STATUS_DENIED BIT(2) +#define ARB_STATUS_DROPPED BIT(3) + +#define ERROR_APID_NOT_FOUND (-(int)BIT(8)) +#define ERROR_TIMEOUT (-(int)BIT(9)) + +#define ARB_COMMAND_TIMEOUT_MS 100 + +// Individual register block per APID +struct qcom_spmi_regs { + uint32_t cmd; + uint32_t config; + uint32_t status; + uint32_t _reserved0; + uint32_t wdata0; + uint32_t wdata1; + uint32_t rdata0; + uint32_t rdata1; + uint8_t _reserved_empty_until_next_apid[SPMI_PMIC_ARB_CHANNEL_SIZE - 0x20]; +}; +check_member(qcom_spmi_regs, rdata1, 0x1c); +_Static_assert(sizeof(struct qcom_spmi_regs) == SPMI_PMIC_ARB_CHANNEL_SIZE, + "struct qcom_spmi_regs must be " STRINGIFY(SPMI_PMIC_ARB_CHANNEL_SIZE) " bytes per APID"); + +struct qcom_spmi { + struct qcom_spmi_regs *regs_per_apid; // indexed by APID + uint32_t *apid_map; + size_t num_apid; +}; + +struct qcom_spmi qcom_spmi = { + (void *) SPMI_PMIC_ARB_CHANNEL_BASE, + (void *) SPMI_PMIC_ARB_APID_MAP_BASE, + SPMI_PMIC_ARB_APID_COUNT +}; + +static struct qcom_spmi_regs *find_apid(uint32_t addr) +{ + size_t i; + + for (i = 0U; i < qcom_spmi.num_apid; i++) { + uint32_t reg = read32(&qcom_spmi.apid_map[i]); + if ((reg != 0U) && ((addr & PPID_MASK) == (reg & PPID_MASK))) + return &qcom_spmi.regs_per_apid[i]; + } + + return NULL; +} + +static int wait_for_done(struct qcom_spmi_regs *regs) +{ + struct stopwatch sw; + + stopwatch_init_msecs_expire(&sw, ARB_COMMAND_TIMEOUT_MS); + while (!stopwatch_expired(&sw)) { + uint32_t status = read32(®s->status); + if ((status & ARB_STATUS_DONE) != 0U) { + if ((status & ARB_STATUS_FAILURE) != 0U || + (status & ARB_STATUS_DENIED) != 0U || + (status & ARB_STATUS_DROPPED) != 0U) + return -(int)(status & 0xff); + return 0; + } + } + printk(BIOS_ERR, "ERROR: SPMI_ARB timeout!\n"); + return ERROR_TIMEOUT; +} + +static void arb_command(struct qcom_spmi_regs *regs, uint8_t opcode, uint32_t addr, + uint8_t bytes) +{ + write32(®s->cmd, (uint32_t)opcode << 27 | + (addr & 0xff) << 4 | (bytes - 1)); +} + +int spmi_read8(uint32_t addr) +{ + struct qcom_spmi_regs *regs = find_apid(addr); + + if (!regs) + return ERROR_APID_NOT_FOUND; + + arb_command(regs, OPC_EXT_READL, addr, 1); + + int ret = wait_for_done(regs); + if (ret != 0) { + printk(BIOS_ERR, "ERROR: SPMI_ARB read error [0x%x]: 0x%x\n", addr, ret); + return ret; + } + + return read32(®s->rdata0) & 0xff; +} + +int spmi_write8(uint32_t addr, uint8_t data) +{ + struct qcom_spmi_regs *regs = find_apid(addr); + + if (!regs) + return ERROR_APID_NOT_FOUND; + + write32(®s->wdata0, data); + arb_command(regs, OPC_EXT_WRITEL, addr, 1); + + int ret = wait_for_done(regs); + if (ret != 0) { + printk(BIOS_ERR, "ERROR: SPMI_ARB write error [0x%x] = 0x%x: 0x%x\n", + addr, data, ret); + } + + return ret; +}