diff options
author | Yidi Lin <yidi.lin@mediatek.com> | 2021-02-02 21:00:36 +0800 |
---|---|---|
committer | Hung-Te Lin <hungte@chromium.org> | 2021-04-28 02:41:43 +0000 |
commit | 2368a310be4bf60ea9c83fc89e89be9d6a040775 (patch) | |
tree | 9c346d5c793178b25d3bfeceb756cbe3ffcb5d75 /src/soc/mediatek/common/pmif.c | |
parent | 15ddb363d4d869fa1c3512e7fbf9682ec41375bf (diff) |
soc/mediatek: Move the common part of PMIC drivers to common/
The PMIC drivers can be shared by MT8192 and MT8195.
Signed-off-by: Yidi Lin <yidi.lin@mediatek.com>
Change-Id: Ie17e01d25405b1e5119d9c70c5f7afb915daf80b
Reviewed-on: https://review.coreboot.org/c/coreboot/+/52666
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Yu-Ping Wu <yupingso@google.com>
Diffstat (limited to 'src/soc/mediatek/common/pmif.c')
-rw-r--r-- | src/soc/mediatek/common/pmif.c | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/src/soc/mediatek/common/pmif.c b/src/soc/mediatek/common/pmif.c new file mode 100644 index 0000000000..a7ab4e5fa5 --- /dev/null +++ b/src/soc/mediatek/common/pmif.c @@ -0,0 +1,304 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <assert.h> +#include <console/console.h> +#include <device/mmio.h> +#include <soc/addressmap.h> +#include <soc/pmif.h> +#include <soc/pmif_spi.h> +#include <soc/pmif_spmi.h> +#include <soc/pmif_sw.h> +#include <soc/spmi.h> +#include <timer.h> + +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); + } +} + +static 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); +} + +static 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); +} + +static 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; +} + +static 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); +} + +static 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); +} + +static 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); +} + +static 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; +} + +static 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); +} + +static int is_pmif_init_done(struct pmif *arb) +{ + if (read32(&arb->mtk_pmif->init_done) & 0x1) + return 0; + + return -E_NODEV; +} + +static const struct pmif pmif_spmi_arb[] = { + { + .mtk_pmif = (struct mtk_pmif_regs *)PMIF_SPMI_BASE, + .ch = (struct chan_regs *)PMIF_SPMI_AP_CHAN, + .mstid = SPMI_MASTER_0, + .pmifid = PMIF_SPMI, + .write = pmif_spmi_write, + .read = pmif_spmi_read, + .write_field = pmif_spmi_write_field, + .read_field = pmif_spmi_read_field, + .is_pmif_init_done = is_pmif_init_done, + }, +}; + +static const struct pmif pmif_spi_arb[] = { + { + .mtk_pmif = (struct mtk_pmif_regs *)PMIF_SPI_BASE, + .ch = (struct chan_regs *)PMIF_SPI_AP_CHAN, + .pmifid = PMIF_SPI, + .write = pmif_spi_write, + .read = pmif_spi_read, + .write_field = pmif_spi_write_field, + .read_field = pmif_spi_read_field, + .is_pmif_init_done = is_pmif_init_done, + }, +}; + +struct pmif *get_pmif_controller(int inf, int mstid) +{ + if (inf == PMIF_SPMI && mstid < ARRAY_SIZE(pmif_spmi_arb)) + 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); + } +} + +int mtk_pmif_init(void) +{ + int ret; + + ret = pmif_clk_init(); + if (!ret) + ret = pmif_spmi_init(get_pmif_controller(PMIF_SPMI, SPMI_MASTER_0)); + if (!ret) + ret = pmif_spi_init(get_pmif_controller(PMIF_SPI, 0)); + + return ret; +} |