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>
327 lines
9.2 KiB
C
327 lines
9.2 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
|
|
#include <console/console.h>
|
|
#include <delay.h>
|
|
#include <device/mmio.h>
|
|
#include <soc/infracfg.h>
|
|
#include <soc/pll.h>
|
|
#include <soc/pmif_spi.h>
|
|
#include <soc/pmif_sw.h>
|
|
#include <timer.h>
|
|
|
|
/* PMIF, SPI_MODE_CTRL */
|
|
DEFINE_BIT(SPI_MODE_CTRL_VLD_SRCLK_EN_CTRL, 5)
|
|
DEFINE_BIT(SPI_MODE_CTRL_PMIF_RDY, 9)
|
|
DEFINE_BIT(SPI_MODE_CTRL_SRCLK_EN, 10)
|
|
DEFINE_BIT(SPI_MODE_CTRL_SRVOL_EN, 11)
|
|
|
|
/* PMIF, SLEEP_PROTECTION_CTRL */
|
|
DEFINE_BITFIELD(SPM_SLEEP_REQ_SEL, 1, 0)
|
|
DEFINE_BITFIELD(SCP_SLEEP_REQ_SEL, 10, 9)
|
|
|
|
/* PMIF, OTHER_INF_EN */
|
|
DEFINE_BITFIELD(INTGPSADCINF_EN, 5, 4)
|
|
|
|
/* PMIF, STAUPD_CTRL */
|
|
DEFINE_BITFIELD(STAUPD_CTRL_PRD, 3, 0)
|
|
DEFINE_BIT(STAUPD_CTRL_PMIC0_SIG_STA, 4)
|
|
DEFINE_BIT(STAUPD_CTRL_PMIC0_EINT_STA, 6)
|
|
|
|
/* SPIMST, Manual_Mode_Access */
|
|
DEFINE_BITFIELD(MAN_ACC_SPI_OP, 12, 8)
|
|
DEFINE_BIT(MAN_ACC_SPI_RW, 13)
|
|
|
|
static void pmif_spi_config(struct pmif *arb)
|
|
{
|
|
/* Set srclk_en always valid regardless of ulposc_sel_for_scp */
|
|
SET32_BITFIELDS(&arb->mtk_pmif->spi_mode_ctrl, SPI_MODE_CTRL_VLD_SRCLK_EN_CTRL, 0);
|
|
|
|
/* Set SPI mode controlled by srclk_en and srvol_en instead of pmif_rdy */
|
|
SET32_BITFIELDS(&arb->mtk_pmif->spi_mode_ctrl,
|
|
SPI_MODE_CTRL_SRCLK_EN, 1,
|
|
SPI_MODE_CTRL_SRVOL_EN, 1,
|
|
SPI_MODE_CTRL_PMIF_RDY, 0);
|
|
|
|
SET32_BITFIELDS(&arb->mtk_pmif->sleep_protection_ctrl, SPM_SLEEP_REQ_SEL, 0,
|
|
SCP_SLEEP_REQ_SEL, 0);
|
|
|
|
/* Enable SWINF for AP */
|
|
write32(&arb->mtk_pmif->inf_en, PMIF_SPI_AP);
|
|
|
|
/* Enable arbitration for SWINF for AP */
|
|
write32(&arb->mtk_pmif->arb_en, PMIF_SPI_AP);
|
|
|
|
/* Enable PMIF_SPI Command Issue */
|
|
write32(&arb->mtk_pmif->cmdissue_en, 1);
|
|
}
|
|
|
|
static int check_idle(void *addr, u32 expected)
|
|
{
|
|
u32 reg_rdata;
|
|
struct stopwatch sw;
|
|
|
|
stopwatch_init_usecs_expire(&sw, PMIF_WAIT_IDLE_US);
|
|
do {
|
|
reg_rdata = read32(addr);
|
|
if (stopwatch_expired(&sw))
|
|
return E_TIMEOUT;
|
|
} while ((reg_rdata & expected) != 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int reset_spislv(void)
|
|
{
|
|
u32 pmicspi_mst_dio_en_backup;
|
|
|
|
write32(&mtk_pmicspi_mst->wrap_en, 0);
|
|
write32(&mtk_pmicspi_mst->mux_sel, 1);
|
|
write32(&mtk_pmicspi_mst->man_en, 1);
|
|
pmicspi_mst_dio_en_backup = read32(&mtk_pmicspi_mst->dio_en);
|
|
write32(&mtk_pmicspi_mst->dio_en, 0);
|
|
|
|
SET32_BITFIELDS(&mtk_pmicspi_mst->man_acc, MAN_ACC_SPI_RW, OP_WR,
|
|
MAN_ACC_SPI_OP, OP_CSL);
|
|
/* Reset counter */
|
|
SET32_BITFIELDS(&mtk_pmicspi_mst->man_acc, MAN_ACC_SPI_RW, OP_WR,
|
|
MAN_ACC_SPI_OP, OP_OUTS);
|
|
SET32_BITFIELDS(&mtk_pmicspi_mst->man_acc, MAN_ACC_SPI_RW, OP_WR,
|
|
MAN_ACC_SPI_OP, OP_CSH);
|
|
/*
|
|
* In order to pull CSN signal to PMIC,
|
|
* PMIC will count it then reset spi slave
|
|
*/
|
|
SET32_BITFIELDS(&mtk_pmicspi_mst->man_acc, MAN_ACC_SPI_RW, OP_WR,
|
|
MAN_ACC_SPI_OP, OP_OUTS);
|
|
SET32_BITFIELDS(&mtk_pmicspi_mst->man_acc, MAN_ACC_SPI_RW, OP_WR,
|
|
MAN_ACC_SPI_OP, OP_OUTS);
|
|
SET32_BITFIELDS(&mtk_pmicspi_mst->man_acc, MAN_ACC_SPI_RW, OP_WR,
|
|
MAN_ACC_SPI_OP, OP_OUTS);
|
|
SET32_BITFIELDS(&mtk_pmicspi_mst->man_acc, MAN_ACC_SPI_RW, OP_WR,
|
|
MAN_ACC_SPI_OP, OP_OUTS);
|
|
|
|
/* Wait for PMIC SPI Master to be idle */
|
|
if (check_idle(&mtk_pmicspi_mst->other_busy_sta_0, SPIMST_STA)) {
|
|
printk(BIOS_ERR, "[%s] spi master busy, timeout\n", __func__);
|
|
return E_TIMEOUT;
|
|
}
|
|
|
|
write32(&mtk_pmicspi_mst->man_en, 0);
|
|
write32(&mtk_pmicspi_mst->mux_sel, 0);
|
|
write32(&mtk_pmicspi_mst->wrap_en, 1);
|
|
write32(&mtk_pmicspi_mst->dio_en, pmicspi_mst_dio_en_backup);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void init_reg_clock(struct pmif *arb)
|
|
{
|
|
pmif_spi_iocfg();
|
|
|
|
/* Configure SPI protocol */
|
|
write32(&mtk_pmicspi_mst->ext_ck_write, 1);
|
|
write32(&mtk_pmicspi_mst->ext_ck_read, 0);
|
|
write32(&mtk_pmicspi_mst->cshext_write, 0);
|
|
write32(&mtk_pmicspi_mst->cshext_read, 0);
|
|
write32(&mtk_pmicspi_mst->cslext_write, 0);
|
|
write32(&mtk_pmicspi_mst->cslext_read, 0x100);
|
|
|
|
/* Set Read Dummy Cycle Number (Slave Clock is 18MHz) */
|
|
arb->write(arb, DEFAULT_SLVID, PMIC_DEW_RDDMY_NO, DUMMY_READ_CYCLES);
|
|
write32(&mtk_pmicspi_mst->rddmy, DUMMY_READ_CYCLES);
|
|
|
|
/* Enable DIO mode */
|
|
arb->write(arb, DEFAULT_SLVID, PMIC_DEW_DIO_EN, 0x1);
|
|
|
|
/* Wait for completion of sending the commands */
|
|
if (check_idle(&arb->mtk_pmif->inf_busy_sta, PMIF_SPI_AP)) {
|
|
printk(BIOS_ERR, "[%s] pmif channel busy, timeout\n", __func__);
|
|
return;
|
|
}
|
|
|
|
if (check_idle(&arb->mtk_pmif->other_busy_sta_0, PMIF_CMD_STA)) {
|
|
printk(BIOS_ERR, "[%s] pmif cmd busy, timeout\n", __func__);
|
|
return;
|
|
}
|
|
|
|
if (check_idle(&mtk_pmicspi_mst->other_busy_sta_0, SPIMST_STA)) {
|
|
printk(BIOS_ERR, "[%s] spi master busy, timeout\n", __func__);
|
|
return;
|
|
}
|
|
|
|
write32(&mtk_pmicspi_mst->dio_en, 1);
|
|
}
|
|
|
|
static void init_spislv(struct pmif *arb)
|
|
{
|
|
/* Turn on SPI IO filter function */
|
|
arb->write(arb, DEFAULT_SLVID, PMIC_FILTER_CON0, SPI_FILTER);
|
|
/* Turn on SPI IO SMT function to improve noise immunity */
|
|
arb->write(arb, DEFAULT_SLVID, PMIC_SMT_CON1, SPI_SMT);
|
|
/* Turn off SPI IO pull function for power saving */
|
|
arb->write(arb, DEFAULT_SLVID, PMIC_GPIO_PULLEN0_CLR, SPI_PULL_DISABLE);
|
|
/* Enable SPI access in SODI-3.0 and Suspend modes */
|
|
arb->write(arb, DEFAULT_SLVID, PMIC_RG_SPI_CON0, 0x2);
|
|
/* Set SPI IO driving strength to 4 mA */
|
|
arb->write(arb, DEFAULT_SLVID, PMIC_DRV_CON1, SPI_DRIVING);
|
|
}
|
|
|
|
static int init_sistrobe(struct pmif *arb)
|
|
{
|
|
u32 rdata = 0;
|
|
int si_sample_ctrl;
|
|
/* Random data for testing */
|
|
const u32 test_data[30] = {
|
|
0x6996, 0x9669, 0x6996, 0x9669, 0x6996, 0x9669, 0x6996,
|
|
0x9669, 0x6996, 0x9669, 0x5AA5, 0xA55A, 0x5AA5, 0xA55A,
|
|
0x5AA5, 0xA55A, 0x5AA5, 0xA55A, 0x5AA5, 0xA55A, 0x1B27,
|
|
0x1B27, 0x1B27, 0x1B27, 0x1B27, 0x1B27, 0x1B27, 0x1B27,
|
|
0x1B27, 0x1B27
|
|
};
|
|
|
|
for (si_sample_ctrl = 0; si_sample_ctrl < 16; si_sample_ctrl++) {
|
|
write32(&mtk_pmicspi_mst->si_sampling_ctrl, si_sample_ctrl << 5);
|
|
|
|
arb->read(arb, DEFAULT_SLVID, PMIC_DEW_READ_TEST, &rdata);
|
|
if (rdata == DEFAULT_VALUE_READ_TEST)
|
|
break;
|
|
}
|
|
|
|
if (si_sample_ctrl == 16)
|
|
return E_CLK_EDGE;
|
|
|
|
if (si_sample_ctrl == 15)
|
|
return E_CLK_LAST_SETTING;
|
|
|
|
/*
|
|
* Add the delay time of SPI data from PMIC to align the start boundary
|
|
* to current sampling clock edge.
|
|
*/
|
|
for (int si_dly = 0; si_dly < 10; si_dly++) {
|
|
arb->write(arb, DEFAULT_SLVID, PMIC_RG_SPI_CON2, si_dly);
|
|
|
|
int start_boundary_found = 0;
|
|
for (int i = 0; i < ARRAY_SIZE(test_data); i++) {
|
|
arb->write(arb, DEFAULT_SLVID, PMIC_DEW_WRITE_TEST, test_data[i]);
|
|
arb->read(arb, DEFAULT_SLVID, PMIC_DEW_WRITE_TEST, &rdata);
|
|
if ((rdata & 0x7fff) != (test_data[i] & 0x7fff)) {
|
|
start_boundary_found = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (start_boundary_found == 1)
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Change the sampling clock edge to the next one which is the middle
|
|
* of SPI data window.
|
|
*/
|
|
write32(&mtk_pmicspi_mst->si_sampling_ctrl, ++si_sample_ctrl << 5);
|
|
|
|
/* Read Test */
|
|
arb->read(arb, DEFAULT_SLVID, PMIC_DEW_READ_TEST, &rdata);
|
|
if (rdata != DEFAULT_VALUE_READ_TEST) {
|
|
printk(BIOS_ERR, "[%s] Failed for read test, data = %#x.\n",
|
|
__func__, rdata);
|
|
return E_READ_TEST_FAIL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void init_staupd(struct pmif *arb)
|
|
{
|
|
/* Unlock SPI Slave registers */
|
|
arb->write(arb, DEFAULT_SLVID, PMIC_SPISLV_KEY, 0xbade);
|
|
|
|
/* Enable CRC of PMIC 0 */
|
|
arb->write(arb, DEFAULT_SLVID, PMIC_DEW_CRC_EN, 0x1);
|
|
|
|
/* Wait for completion of sending the commands */
|
|
if (check_idle(&arb->mtk_pmif->inf_busy_sta, PMIF_SPI_AP)) {
|
|
printk(BIOS_ERR, "[%s] pmif channel busy, timeout\n", __func__);
|
|
return;
|
|
}
|
|
|
|
if (check_idle(&arb->mtk_pmif->other_busy_sta_0, PMIF_CMD_STA)) {
|
|
printk(BIOS_ERR, "[%s] pmif cmd busy, timeout\n", __func__);
|
|
return;
|
|
}
|
|
|
|
if (check_idle(&mtk_pmicspi_mst->other_busy_sta_0, SPIMST_STA)) {
|
|
printk(BIOS_ERR, "[%s] spi master busy, timeout\n", __func__);
|
|
return;
|
|
}
|
|
|
|
/* Configure CRC of PMIC Interface */
|
|
write32(&arb->mtk_pmif->crc_ctrl, 0x1);
|
|
write32(&arb->mtk_pmif->sig_mode, 0x0);
|
|
|
|
/* Lock SPI Slave registers */
|
|
arb->write(arb, DEFAULT_SLVID, PMIC_SPISLV_KEY, 0x0);
|
|
|
|
/* Set up PMIC Siganature */
|
|
write32(&arb->mtk_pmif->pmic_sig_addr, PMIC_DEW_CRC_VAL);
|
|
|
|
/* Set up PMIC EINT */
|
|
write32(&arb->mtk_pmif->pmic_eint_sta_addr, PMIC_INT_STA);
|
|
|
|
SET32_BITFIELDS(&arb->mtk_pmif->staupd_ctrl,
|
|
STAUPD_CTRL_PRD, 5,
|
|
STAUPD_CTRL_PMIC0_SIG_STA, 1,
|
|
STAUPD_CTRL_PMIC0_EINT_STA, 1);
|
|
}
|
|
|
|
int pmif_spi_init(struct pmif *arb)
|
|
{
|
|
pmif_spi_config(arb);
|
|
|
|
/* Reset spislv */
|
|
if (reset_spislv())
|
|
return E_SPI_INIT_RESET_SPI;
|
|
|
|
/* Enable WRAP */
|
|
write32(&mtk_pmicspi_mst->wrap_en, 0x1);
|
|
|
|
/* SPI Waveform Configuration */
|
|
init_reg_clock(arb);
|
|
|
|
/* SPI Slave Configuration */
|
|
init_spislv(arb);
|
|
|
|
/* Input data calibration flow; */
|
|
if (init_sistrobe(arb)) {
|
|
printk(BIOS_ERR, "[%s] data calibration fail\n", __func__);
|
|
return E_SPI_INIT_SIDLY;
|
|
}
|
|
|
|
/* Lock SPISLV Registers */
|
|
arb->write(arb, DEFAULT_SLVID, PMIC_SPISLV_KEY, 0x0);
|
|
|
|
/*
|
|
* Status update function initialization
|
|
* 1. Check signature using CRC (CRC 0 only)
|
|
* 2. Update EINT
|
|
* 3. Read back AUXADC thermal data for GPS
|
|
*/
|
|
init_staupd(arb);
|
|
|
|
/* Configure PMIF Timer */
|
|
write32(&arb->mtk_pmif->timer_ctrl, 0x3);
|
|
|
|
/* Enable interfaces and arbitration */
|
|
write32(&arb->mtk_pmif->inf_en, PMIF_SPI_HW_INF | PMIF_SPI_MD |
|
|
PMIF_SPI_AP_SECURE | PMIF_SPI_AP);
|
|
|
|
write32(&arb->mtk_pmif->arb_en, PMIF_SPI_HW_INF | PMIF_SPI_MD | PMIF_SPI_AP_SECURE |
|
|
PMIF_SPI_AP | PMIF_SPI_STAUPD | PMIF_SPI_TSX_HW | PMIF_SPI_DCXO_HW);
|
|
|
|
/* Enable GPS AUXADC HW 0 and 1 */
|
|
SET32_BITFIELDS(&arb->mtk_pmif->other_inf_en, INTGPSADCINF_EN, 0x3);
|
|
|
|
/* Set INIT_DONE */
|
|
write32(&arb->mtk_pmif->init_done, 0x1);
|
|
|
|
return 0;
|
|
}
|