/* SPDX-License-Identifier: GPL-2.0-only */

#include <console/console.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;
}