/* SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #include #include #include #include #include #include static int pmif_check_swinf(struct pmif *arb, long timeout_us, u32 expected_status) { u32 reg_rdata; struct stopwatch sw; stopwatch_init_usecs_expire(&sw, timeout_us); do { reg_rdata = read32(&arb->ch->ch_sta); if (stopwatch_expired(&sw)) return E_TIMEOUT; } while (GET_SWINF_0_FSM(reg_rdata) != expected_status); return 0; } static void pmif_send_cmd(struct pmif *arb, int write, u32 opc, u32 slvid, u32 addr, u32 *rdata, u32 wdata, u32 len) { int ret; u32 data, bc = len - 1; /* Wait for Software Interface FSM state to be IDLE. */ ret = pmif_check_swinf(arb, PMIF_WAIT_IDLE_US, SWINF_FSM_IDLE); if (ret) { printk(BIOS_ERR, "[%s] idle timeout\n", __func__); return; } /* Set the write data */ if (write) write32(&arb->ch->wdata, wdata); /* Send the command. */ write32(&arb->ch->ch_send, (opc << 30) | (write << 29) | (slvid << 24) | (bc << 16) | addr); if (!write) { /* * Wait for Software Interface FSM state to be WFVLDCLR, * read the data and clear the valid flag. */ ret = pmif_check_swinf(arb, PMIF_READ_US, SWINF_FSM_WFVLDCLR); if (ret) { printk(BIOS_ERR, "[%s] read timeout\n", __func__); return; } data = read32(&arb->ch->rdata); *rdata = data; write32(&arb->ch->ch_rdy, 0x1); } } void pmif_spmi_read(struct pmif *arb, u32 slvid, u32 reg, u32 *data) { *data = 0; pmif_send_cmd(arb, 0, PMIF_CMD_EXT_REG_LONG, slvid, reg, data, 0, 1); } void pmif_spmi_write(struct pmif *arb, u32 slvid, u32 reg, u32 data) { pmif_send_cmd(arb, 1, PMIF_CMD_EXT_REG_LONG, slvid, reg, NULL, data, 1); } u32 pmif_spmi_read_field(struct pmif *arb, u32 slvid, u32 reg, u32 mask, u32 shift) { u32 data; pmif_spmi_read(arb, slvid, reg, &data); data &= (mask << shift); data >>= shift; return data; } void pmif_spmi_write_field(struct pmif *arb, u32 slvid, u32 reg, u32 val, u32 mask, u32 shift) { u32 old, new; pmif_spmi_read(arb, slvid, reg, &old); new = old & ~(mask << shift); new |= (val << shift); pmif_spmi_write(arb, slvid, reg, new); } void pmif_spi_read(struct pmif *arb, u32 slvid, u32 reg, u32 *data) { *data = 0; pmif_send_cmd(arb, 0, PMIF_CMD_REG_0, slvid, reg, data, 0, 1); } void pmif_spi_write(struct pmif *arb, u32 slvid, u32 reg, u32 data) { pmif_send_cmd(arb, 1, PMIF_CMD_REG_0, slvid, reg, NULL, data, 1); } u32 pmif_spi_read_field(struct pmif *arb, u32 slvid, u32 reg, u32 mask, u32 shift) { u32 data; pmif_spi_read(arb, slvid, reg, &data); data &= (mask << shift); data >>= shift; return data; } void pmif_spi_write_field(struct pmif *arb, u32 slvid, u32 reg, u32 val, u32 mask, u32 shift) { u32 old, new; pmif_spi_read(arb, slvid, reg, &old); new = old & ~(mask << shift); new |= (val << shift); pmif_spi_write(arb, slvid, reg, new); } int pmif_check_init_done(struct pmif *arb) { if (read32(&arb->mtk_pmif->init_done) & 0x1) return 0; return -E_NODEV; } struct pmif *get_pmif_controller(int inf, int mstid) { if (inf == PMIF_SPMI && mstid < pmif_spmi_arb_count) return (struct pmif *)&pmif_spmi_arb[mstid]; else if (inf == PMIF_SPI) return (struct pmif *)&pmif_spi_arb[0]; die("[%s] Failed to get pmif controller: inf = %d, mstid = %d\n", __func__, inf, mstid); return NULL; } static void pmif_select(enum pmic_interface mode) { unsigned int spi_spm_sleep_req, spi_scp_sleep_req, spmi_spm_sleep_req, spmi_scp_sleep_req, spi_md_ctl_pmif_rdy, spi_md_ctl_srclk_en, spi_md_ctl_srvol_en, spmi_md_ctl_pmif_rdy, spmi_md_ctl_srclk_en, spmi_md_ctl_srvol_en, spi_inf_srclken_rc_en, spi_other_inf_dcxo0_en, spi_other_inf_dcxo1_en, spi_arb_srclken_rc_en, spi_arb_dcxo_conn_en, spi_arb_dcxo_nfc_en; switch (mode) { case PMIF_VLD_RDY: /* spm and scp sleep request disable spi and spmi */ spi_spm_sleep_req = 1; spi_scp_sleep_req = 1; spmi_spm_sleep_req = 1; spmi_scp_sleep_req = 1; /* * pmic vld/rdy control spi mode enable * srclken control spi mode disable * vreq control spi mode disable */ spi_md_ctl_pmif_rdy = 1; spi_md_ctl_srclk_en = 0; spi_md_ctl_srvol_en = 0; spmi_md_ctl_pmif_rdy = 1; spmi_md_ctl_srclk_en = 0; spmi_md_ctl_srvol_en = 0; /* srclken rc interface enable */ spi_inf_srclken_rc_en = 1; /* dcxo interface disable */ spi_other_inf_dcxo0_en = 0; spi_other_inf_dcxo1_en = 0; /* srclken enable, dcxo0,1 disable */ spi_arb_srclken_rc_en = 1; spi_arb_dcxo_conn_en = 0; spi_arb_dcxo_nfc_en = 0; break; case PMIF_SLP_REQ: /* spm and scp sleep request enable spi and spmi */ spi_spm_sleep_req = 0; spi_scp_sleep_req = 0; spmi_spm_sleep_req = 0; spmi_scp_sleep_req = 0; /* * pmic vld/rdy control spi mode disable * srclken control spi mode enable * vreq control spi mode enable */ spi_md_ctl_pmif_rdy = 0; spi_md_ctl_srclk_en = 1; spi_md_ctl_srvol_en = 1; spmi_md_ctl_pmif_rdy = 0; spmi_md_ctl_srclk_en = 1; spmi_md_ctl_srvol_en = 1; /* srclken rc interface disable */ spi_inf_srclken_rc_en = 0; /* dcxo interface enable */ spi_other_inf_dcxo0_en = 1; spi_other_inf_dcxo1_en = 1; /* srclken disable, dcxo0,1 enable */ spi_arb_srclken_rc_en = 0; spi_arb_dcxo_conn_en = 1; spi_arb_dcxo_nfc_en = 1; break; default: die("Can't support pmif mode %d\n", mode); } SET32_BITFIELDS(&pmif_spi_arb[0].mtk_pmif->sleep_protection_ctrl, PMIFSPI_SPM_SLEEP_REQ_SEL, spi_spm_sleep_req, PMIFSPI_SCP_SLEEP_REQ_SEL, spi_scp_sleep_req); SET32_BITFIELDS(&pmif_spmi_arb[0].mtk_pmif->sleep_protection_ctrl, PMIFSPMI_SPM_SLEEP_REQ_SEL, spmi_spm_sleep_req, PMIFSPMI_SCP_SLEEP_REQ_SEL, spmi_scp_sleep_req); SET32_BITFIELDS(&pmif_spi_arb[0].mtk_pmif->spi_mode_ctrl, PMIFSPI_MD_CTL_PMIF_RDY, spi_md_ctl_pmif_rdy, PMIFSPI_MD_CTL_SRCLK_EN, spi_md_ctl_srclk_en, PMIFSPI_MD_CTL_SRVOL_EN, spi_md_ctl_srvol_en); SET32_BITFIELDS(&pmif_spmi_arb[0].mtk_pmif->spi_mode_ctrl, PMIFSPMI_MD_CTL_PMIF_RDY, spmi_md_ctl_pmif_rdy, PMIFSPMI_MD_CTL_SRCLK_EN, spmi_md_ctl_srclk_en, PMIFSPMI_MD_CTL_SRVOL_EN, spmi_md_ctl_srvol_en); SET32_BITFIELDS(&pmif_spi_arb[0].mtk_pmif->inf_en, PMIFSPI_INF_EN_SRCLKEN_RC_HW, spi_inf_srclken_rc_en); SET32_BITFIELDS(&pmif_spi_arb[0].mtk_pmif->other_inf_en, PMIFSPI_OTHER_INF_DXCO0_EN, spi_other_inf_dcxo0_en, PMIFSPI_OTHER_INF_DXCO1_EN, spi_other_inf_dcxo1_en); SET32_BITFIELDS(&pmif_spi_arb[0].mtk_pmif->arb_en, PMIFSPI_ARB_EN_SRCLKEN_RC_HW, spi_arb_srclken_rc_en, PMIFSPI_ARB_EN_DCXO_CONN, spi_arb_dcxo_conn_en, PMIFSPI_ARB_EN_DCXO_NFC, spi_arb_dcxo_nfc_en); } void pmwrap_interface_init(void) { if (CONFIG(SRCLKEN_RC_SUPPORT)) { printk(BIOS_INFO, "%s: Select PMIF_VLD_RDY\n", __func__); pmif_select(PMIF_VLD_RDY); } else { printk(BIOS_INFO, "%s: Select PMIF_SLP_REQ\n", __func__); pmif_select(PMIF_SLP_REQ); } }