aboutsummaryrefslogtreecommitdiff
path: root/src/soc/mediatek/mt8192/pmif_spi.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/soc/mediatek/mt8192/pmif_spi.c')
-rw-r--r--src/soc/mediatek/mt8192/pmif_spi.c332
1 files changed, 332 insertions, 0 deletions
diff --git a/src/soc/mediatek/mt8192/pmif_spi.c b/src/soc/mediatek/mt8192/pmif_spi.c
new file mode 100644
index 0000000000..72620041bd
--- /dev/null
+++ b/src/soc/mediatek/mt8192/pmif_spi.c
@@ -0,0 +1,332 @@
+/* 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.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)
+
+/* IOCFG_LM, PWRAP_SPI0_DRIVING */
+DEFINE_BITFIELD(PWRAP_SPI0_DRIVING, 2, 0)
+
+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)
+{
+ /* Set SoC SPI IO driving strength to 4 mA */
+ SET32_BITFIELDS(&mtk_iocfg_lm->drv_cfg1, PWRAP_SPI0_DRIVING, IO_4_MA);
+
+ /* 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;
+}