/* * Copyright (c) 2025, MediaTek Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #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); }