2368a310be
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>
304 lines
7.9 KiB
C
304 lines
7.9 KiB
C
/* 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;
|
|
}
|