summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/mainboard/google/kukui/mainboard.c4
-rw-r--r--src/soc/mediatek/mt8183/Makefile.inc15
-rw-r--r--src/soc/mediatek/mt8183/include/soc/spm.h186
-rw-r--r--src/soc/mediatek/mt8183/spm.c351
4 files changed, 507 insertions, 49 deletions
diff --git a/src/mainboard/google/kukui/mainboard.c b/src/mainboard/google/kukui/mainboard.c
index 7b00d94ba66e..844496d7a0de 100644
--- a/src/mainboard/google/kukui/mainboard.c
+++ b/src/mainboard/google/kukui/mainboard.c
@@ -28,6 +28,7 @@
#include <soc/gpio.h>
#include <soc/mmu_operations.h>
#include <soc/mtcmos.h>
+#include <soc/spm.h>
#include <soc/usb.h>
#include <string.h>
@@ -201,6 +202,9 @@ static void mainboard_init(struct device *dev)
configure_emmc();
configure_usb();
configure_audio();
+ if (spm_init())
+ printk(BIOS_ERR,
+ "SPM initialization failed, suspend/resume may fail.\n");
register_reset_to_bl31();
}
diff --git a/src/soc/mediatek/mt8183/Makefile.inc b/src/soc/mediatek/mt8183/Makefile.inc
index b6c3a3381906..d1171ef37978 100644
--- a/src/soc/mediatek/mt8183/Makefile.inc
+++ b/src/soc/mediatek/mt8183/Makefile.inc
@@ -55,6 +55,7 @@ ramstage-y += ../common/pmic_wrap.c
ramstage-y += ../common/rtc.c rtc.c
ramstage-y += soc.c
ramstage-$(CONFIG_SPI_FLASH) += ../common/spi.c spi.c
+ramstage-y += spm.c
ramstage-y += sspm.c
ramstage-y += ../common/timer.c
ramstage-y += ../common/uart.c
@@ -62,8 +63,20 @@ ramstage-y += ../common/usb.c
ramstage-y += ../common/wdt.c
ramstage-y += md_ctrl.c
+MT8183_BLOB_DIR := 3rdparty/blobs/soc/mediatek/mt8183
+
+cbfs-files-y += pcm_allinone_lp4_3200.bin
+pcm_allinone_lp4_3200.bin-file := $(MT8183_BLOB_DIR)/pcm_allinone_lp4_3200.bin
+pcm_allinone_lp4_3200.bin-type := raw
+pcm_allinone_lp4_3200.bin-compression := $(CBFS_COMPRESS_FLAG)
+
+cbfs-files-y += pcm_allinone_lp4_3733.bin
+pcm_allinone_lp4_3733.bin-file := $(MT8183_BLOB_DIR)/pcm_allinone_lp4_3733.bin
+pcm_allinone_lp4_3733.bin-type := raw
+pcm_allinone_lp4_3733.bin-compression := $(CBFS_COMPRESS_FLAG)
+
cbfs-files-y += sspm.bin
-sspm.bin-file := 3rdparty/blobs/soc/mediatek/mt8183/sspm.bin
+sspm.bin-file := $(MT8183_BLOB_DIR)/sspm.bin
sspm.bin-type := raw
sspm.bin-compression := $(CBFS_COMPRESS_FLAG)
diff --git a/src/soc/mediatek/mt8183/include/soc/spm.h b/src/soc/mediatek/mt8183/include/soc/spm.h
index 5e7770eecbc0..4ca72b632a84 100644
--- a/src/soc/mediatek/mt8183/include/soc/spm.h
+++ b/src/soc/mediatek/mt8183/include/soc/spm.h
@@ -16,12 +16,122 @@
#ifndef SOC_MEDIATEK_MT8183_SPM_H
#define SOC_MEDIATEK_MT8183_SPM_H
+#include <arch/barrier.h>
+#include <console/console.h>
#include <soc/addressmap.h>
+#include <string.h>
+#include <stdint.h>
#include <types.h>
-enum {
- SPM_PROJECT_CODE = 0xb16
-};
+/* SPM READ/WRITE CFG */
+#define SPM_PROJECT_CODE 0xb16
+#define SPM_REGWR_CFG_KEY (SPM_PROJECT_CODE << 16)
+
+/* POWERON_CONFIG_EN (0x10006000+0x000) */
+#define BCLK_CG_EN_LSB (1U << 0) /* 1b */
+#define MD_BCLK_CG_EN_LSB (1U << 1) /* 1b */
+#define PCM_IM_HOST_W_EN_LSB (1U << 30) /* 1b */
+#define PCM_IM_HOST_EN_LSB (1U << 31) /* 1b */
+
+/* SPM_CLK_CON (0x10006000+0x00C) */
+#define SYSCLK0_EN_CTRL_LSB (1U << 0) /* 2b */
+#define SYSCLK1_EN_CTRL_LSB (1U << 2) /* 2b */
+#define SPM_LOCK_INFRA_DCM_LSB (1U << 5) /* 1b */
+#define EXT_SRCCLKEN_MASK (1U << 6) /* 1b */
+#define CXO32K_REMOVE_EN_MD1_LSB (1U << 9) /* 1b */
+#define CLKSQ1_SEL_CTRL_LSB (1U << 12) /* 1b */
+#define SRCLKEN0_EN_LSB (1U << 13) /* 1b */
+
+/* PCM_CON0 (0x10006000+0x018) */
+#define PCM_KICK_L_LSB (1U << 0) /* 1b */
+#define IM_KICK_L_LSB (1U << 1) /* 1b */
+#define PCM_CK_EN_LSB (1U << 2) /* 1b */
+#define EN_IM_SLEEP_DVS_LSB (1U << 3) /* 1b */
+#define IM_AUTO_PDN_EN_LSB (1U << 4) /* 1b */
+#define PCM_SW_RESET_LSB (1U << 15) /* 1b */
+
+/* PCM_CON1 (0x10006000+0x01C) */
+#define IM_SLAVE_LSB (1U << 0) /* 1b */
+#define IM_SLEEP_LSB (1U << 1) /* 1b */
+#define MIF_APBEN_LSB (1U << 3) /* 1b */
+#define IM_PDN_LSB (1U << 4) /* 1b */
+#define PCM_TIMER_EN_LSB (1U << 5) /* 1b */
+#define IM_NONRP_EN_LSB (1U << 6) /* 1b */
+#define DIS_MIF_PROT_LSB (1U << 7) /* 1b */
+#define PCM_WDT_EN_LSB (1U << 8) /* 1b */
+#define PCM_WDT_WAKE_MODE_LSB (1U << 9) /* 1b */
+#define SPM_SRAM_SLEEP_B_LSB (1U << 10) /* 1b */
+#define SPM_SRAM_ISOINT_B_LSB (1U << 11) /* 1b */
+#define EVENT_LOCK_EN_LSB (1U << 12) /* 1b */
+#define SRCCLKEN_FAST_RESP_LSB (1U << 13) /* 1b */
+#define SCP_APB_INTERNAL_EN_LSB (1U << 14) /* 1b */
+
+/* SPM_IRQ_MASK (0x10006000+0x0B4) */
+#define PCM_IRQ_ROOT_MASK_LSB (1U << 3) /* 1b */
+
+/* SPM_WAKEUP_EVENT_MASK (0x10006000+0x0C4) */
+#define WAKEUP_EVENT_MASK_B_BIT0 (1U << 0) /* 1b */
+
+/* SPARE_SRC_REQ_MASK (0x10006000+0x6C0) */
+#define SPARE1_DDREN_MASK_B_LSB (1U << 0) /* 1b */
+
+/* SPM_PC_TRACE_CON (0x10006000+0x8C0) */
+#define SPM_PC_TRACE_OFFSET_LSB (1U << 0) /* 12b */
+#define SPM_PC_TRACE_OFFSET (1U << 3) /* 1b */
+#define SPM_PC_TRACE_HW_EN_LSB (1U << 16) /* 1b */
+
+/* SPM_SPARE_ACK_MASK (0x10006000+0x6F4) */
+#define SPARE_ACK_MASK_B_BIT0 (1U << 0) /* 1b */
+#define SPARE_ACK_MASK_B_BIT1 (1U << 1) /* 1b */
+
+/**************************************
+ * Config and Parameter
+ **************************************/
+#define CONN_DDR_EN_DBC_LEN (0x00000154 << 20)
+#define IFR_SRAMROM_ROM_PDN (0x0000003f)
+#define IM_STATE (0x4 << 7)
+#define IM_STATE_MASK (0x7 << 7)
+#define MD_DDR_EN_0_DBC_LEN (0x00000154)
+#define MD_DDR_EN_1_DBC_LEN (0x00000154 << 10)
+#define PCM_FSM_STA_DEF (0x00108490)
+#define PCM_FSM_STA_MASK (0x7FFFFF)
+#define POWER_ON_VAL1_DEF (0x00015800)
+#define SPM_CORE_TIMEOUT (5000)
+#define SPM_MAS_PAUSE_MASK_B_VAL (0xFFFFFFFF)
+#define SPM_MAS_PAUSE2_MASK_B_VAL (0xFFFFFFFF)
+#define SPM_PCM_REG1_DATA_CHECK (0x1)
+#define SPM_PCM_REG15_DATA_CHECK (0x0)
+#define SPM_WAKEUP_EVENT_MASK_DEF (0xF0F92218)
+#define SYSCLK1_EN_CTRL (0x3 << 2)
+#define SYSCLK1_SRC_MASK_B (0x10 << 23)
+
+/**************************************
+ * Define and Declare
+ **************************************/
+/* SPM_IRQ_MASK */
+#define ISRM_TWAM (1U << 2)
+#define ISRM_PCM_RETURN (1U << 3)
+#define ISRM_RET_IRQ_AUX (0x3FF00)
+#define ISRM_ALL_EXC_TWAM (ISRM_RET_IRQ_AUX)
+#define ISRM_ALL (ISRM_ALL_EXC_TWAM | ISRM_TWAM)
+
+/* SPM_IRQ_STA */
+#define ISRS_TWAM (1U << 2)
+#define ISRS_PCM_RETURN (1U << 3)
+#define ISRS_SW_INT0 (1U << 4)
+#define ISRC_TWAM (ISRS_TWAM)
+#define ISRC_ALL_EXC_TWAM (ISRS_PCM_RETURN)
+#define ISRC_ALL (ISRC_ALL_EXC_TWAM | ISRC_TWAM)
+
+/* PCM_PWR_IO_EN */
+#define PCM_PWRIO_EN_R0 (1U << 0)
+#define PCM_PWRIO_EN_R7 (1U << 7)
+#define PCM_RF_SYNC_R0 (1U << 16)
+#define PCM_RF_SYNC_R6 (1U << 22)
+#define PCM_RF_SYNC_R7 (1U << 23)
+
+/* SPM_SWINT */
+#define PCM_SW_INT_ALL (0x3FF)
enum {
DISP_SRAM_PDN_MASK = 0x1 << 8,
@@ -30,6 +140,8 @@ enum {
AUDIO_SRAM_ACK_MASK = 0xf << 12,
};
+#define PCM_EVENT_VECTOR_NUM 16
+
struct mtk_spm_regs {
u32 poweron_config_set;
u32 spm_power_on_val0;
@@ -47,22 +159,7 @@ struct mtk_spm_regs {
u32 pcm_wdt_val;
u32 pcm_im_host_rw_ptr;
u32 pcm_im_host_rw_dat;
- u32 pcm_event_vector0;
- u32 pcm_event_vector1;
- u32 pcm_event_vector2;
- u32 pcm_event_vector3;
- u32 pcm_event_vector4;
- u32 pcm_event_vector5;
- u32 pcm_event_vector6;
- u32 pcm_event_vector7;
- u32 pcm_event_vector8;
- u32 pcm_event_vector9;
- u32 pcm_event_vector10;
- u32 pcm_event_vector11;
- u32 pcm_event_vector12;
- u32 pcm_event_vector13;
- u32 pcm_event_vector14;
- u32 pcm_event_vector15;
+ u32 pcm_event_vector[PCM_EVENT_VECTOR_NUM];
u32 pcm_event_vector_en;
u32 reserved1[1];
u32 spm_sram_rsv_con;
@@ -472,37 +569,30 @@ struct mtk_spm_regs {
u32 spm_ack_chk_sta4;
u32 spm_ack_chk_latch4;
};
-
-check_member(mtk_spm_regs, pcm_reg0_data, 0x0100);
-check_member(mtk_spm_regs, src_ddren_sta, 0x01e0);
-check_member(mtk_spm_regs, mcu_pwr_con, 0x0200);
-check_member(mtk_spm_regs, mp0_cputop_l2_pdn, 0x0240);
-check_member(mtk_spm_regs, cpu_ext_buck_iso, 0x0290);
-check_member(mtk_spm_regs, dummy1_pwr_con, 0x02b0);
-check_member(mtk_spm_regs, vde_pwr_con, 0x0300);
-check_member(mtk_spm_regs, ufs_sram_con, 0x036c);
-check_member(mtk_spm_regs, dummy_sram_con, 0x0380);
-check_member(mtk_spm_regs, md_ext_buck_iso_con, 0x0390);
-check_member(mtk_spm_regs, mbist_efuse_repair_ack_sta, 0x03d0);
-check_member(mtk_spm_regs, spm_dvfs_con, 0x0400);
-check_member(mtk_spm_regs, mp0_cpu0_wfi_en, 0x0530);
-check_member(mtk_spm_regs, root_cputop_addr, 0x0570);
-check_member(mtk_spm_regs, cpu_spare_con, 0x0580);
-check_member(mtk_spm_regs, spm2sw_mailbox_0, 0x05d0);
-check_member(mtk_spm_regs, spm_sw_rsv_18, 0x067c);
-check_member(mtk_spm_regs, dvfsrc_event_mask_con, 0x0690);
-check_member(mtk_spm_regs, spare_ack_sta, 0x06f0);
-check_member(mtk_spm_regs, spm_dvfs_con1, 0x0700);
-check_member(mtk_spm_regs, spm_dvfs_cmd0, 0x0710);
-check_member(mtk_spm_regs, wdt_latch_spare0_fix, 0x0780);
-check_member(mtk_spm_regs, pcm_wdt_latch_0, 0x0800);
-check_member(mtk_spm_regs, spm_pc_trace_con, 0x08c0);
-check_member(mtk_spm_regs, spm_ack_chk_con, 0x0900);
-check_member(mtk_spm_regs, spm_ack_chk_con2, 0x0920);
-check_member(mtk_spm_regs, spm_ack_chk_con3, 0x0940);
-check_member(mtk_spm_regs, spm_ack_chk_con4, 0x0960);
check_member(mtk_spm_regs, spm_ack_chk_latch4, 0x0974);
static struct mtk_spm_regs *const mtk_spm = (void *)SPM_BASE;
+enum dyna_load_pcm_index {
+ DYNA_LOAD_PCM_SUSPEND_LP4_3733 = 0,
+ DYNA_LOAD_PCM_SUSPEND_LP4_3200,
+ DYNA_LOAD_PCM_MAX,
+};
+
+struct pcm_desc {
+ u16 size; /* binary array size */
+ u8 sess; /* session number */
+ u8 replace; /* replace mode */
+ u16 addr_2nd; /* 2nd binary array size */
+ u16 reserved; /* for 32bit alignment */
+ u32 vector[PCM_EVENT_VECTOR_NUM]; /* event vector config */
+};
+
+struct dyna_load_pcm {
+ u32 *buf; /* binary array */
+ struct pcm_desc desc;
+};
+
+int spm_init(void);
+
#endif /* SOC_MEDIATEK_MT8183_SPM_H */
diff --git a/src/soc/mediatek/mt8183/spm.c b/src/soc/mediatek/mt8183/spm.c
new file mode 100644
index 000000000000..669970fb2f77
--- /dev/null
+++ b/src/soc/mediatek/mt8183/spm.c
@@ -0,0 +1,351 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2019 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <assert.h>
+#include <cbfs.h>
+#include <delay.h>
+#include <device/mmio.h>
+#include <endian.h>
+#include <soc/emi.h>
+#include <soc/spm.h>
+#include <timer.h>
+
+#define BUF_SIZE (16 * KiB)
+static uint8_t spm_bin[BUF_SIZE] __aligned(8);
+
+static int spm_register_init(void)
+{
+ u32 pcm_fsm_sta;
+
+ write32(&mtk_spm->poweron_config_set,
+ SPM_REGWR_CFG_KEY | BCLK_CG_EN_LSB | MD_BCLK_CG_EN_LSB);
+
+ write32(&mtk_spm->spm_power_on_val1, POWER_ON_VAL1_DEF);
+ write32(&mtk_spm->pcm_pwr_io_en, 0);
+
+ write32(&mtk_spm->pcm_con0, SPM_REGWR_CFG_KEY | PCM_CK_EN_LSB |
+ PCM_SW_RESET_LSB);
+ write32(&mtk_spm->pcm_con0, SPM_REGWR_CFG_KEY | PCM_CK_EN_LSB);
+
+ pcm_fsm_sta = read32(&mtk_spm->pcm_fsm_sta);
+
+ if ((pcm_fsm_sta & PCM_FSM_STA_MASK) != PCM_FSM_STA_DEF) {
+ printk(BIOS_ERR, "PCM reset failed\n");
+ return -1;
+ }
+
+ write32(&mtk_spm->pcm_con0, SPM_REGWR_CFG_KEY | PCM_CK_EN_LSB |
+ EN_IM_SLEEP_DVS_LSB);
+ write32(&mtk_spm->pcm_con1, SPM_REGWR_CFG_KEY | EVENT_LOCK_EN_LSB |
+ SPM_SRAM_ISOINT_B_LSB | MIF_APBEN_LSB |
+ SCP_APB_INTERNAL_EN_LSB);
+ write32(&mtk_spm->pcm_im_ptr, 0);
+ write32(&mtk_spm->pcm_im_len, 0);
+
+ write32(&mtk_spm->spm_clk_con,
+ read32(&mtk_spm->spm_clk_con) | SYSCLK1_EN_CTRL |
+ SPM_LOCK_INFRA_DCM_LSB | EXT_SRCCLKEN_MASK |
+ CXO32K_REMOVE_EN_MD1_LSB |
+ CLKSQ1_SEL_CTRL_LSB | SRCLKEN0_EN_LSB | SYSCLK1_SRC_MASK_B);
+
+ write32(&mtk_spm->spm_wakeup_event_mask, SPM_WAKEUP_EVENT_MASK_DEF);
+
+ write32(&mtk_spm->spm_irq_mask, ISRM_ALL);
+ write32(&mtk_spm->spm_irq_sta, ISRC_ALL);
+ write32(&mtk_spm->spm_swint_clr, PCM_SW_INT_ALL);
+
+ write32(&mtk_spm->pcm_reg_data_ini,
+ read32(&mtk_spm->spm_power_on_val1));
+ write32(&mtk_spm->pcm_pwr_io_en, PCM_RF_SYNC_R7);
+ write32(&mtk_spm->pcm_pwr_io_en, 0);
+
+ write32(&mtk_spm->ddr_en_dbc_len,
+ MD_DDR_EN_0_DBC_LEN |
+ MD_DDR_EN_1_DBC_LEN |
+ CONN_DDR_EN_DBC_LEN);
+
+ clrsetbits_le32(&mtk_spm->spare_ack_mask,
+ SPARE_ACK_MASK_B_BIT1,
+ SPARE_ACK_MASK_B_BIT0);
+
+ write32(&mtk_spm->sysrom_con, IFR_SRAMROM_ROM_PDN);
+ write32(&mtk_spm->spm_pc_trace_con,
+ SPM_PC_TRACE_OFFSET |
+ SPM_PC_TRACE_HW_EN_LSB);
+
+ setbits_le32(&mtk_spm->spare_src_req_mask, SPARE1_DDREN_MASK_B_LSB);
+
+ return 0;
+}
+
+static int spm_code_swapping(void)
+{
+ u32 con1;
+
+ con1 = read32(&mtk_spm->spm_wakeup_event_mask);
+
+ write32(&mtk_spm->spm_wakeup_event_mask,
+ con1 & ~WAKEUP_EVENT_MASK_B_BIT0);
+ write32(&mtk_spm->spm_cpu_wakeup_event, 1);
+
+ if (!wait_us(SPM_CORE_TIMEOUT,
+ read32(&mtk_spm->spm_irq_sta) & PCM_IRQ_ROOT_MASK_LSB)) {
+ printk(BIOS_ERR,
+ "timeout: r15=%#x, pcmsta=%#x, irqsta=%#x [%d]\n",
+ read32(&mtk_spm->pcm_reg15_data),
+ read32(&mtk_spm->pcm_fsm_sta),
+ read32(&mtk_spm->spm_irq_sta),
+ SPM_CORE_TIMEOUT);
+ return -1;
+ }
+
+ write32(&mtk_spm->spm_cpu_wakeup_event, 0);
+ write32(&mtk_spm->spm_wakeup_event_mask, con1);
+ return 0;
+}
+
+static int spm_reset_and_init_pcm(const struct pcm_desc *pcmdesc)
+{
+ u32 con1, pcm_fsm_sta;
+
+ if (read32(&mtk_spm->pcm_reg1_data) == SPM_PCM_REG1_DATA_CHECK &&
+ read32(&mtk_spm->pcm_reg15_data) != SPM_PCM_REG15_DATA_CHECK) {
+ if (spm_code_swapping())
+ return -1;
+ write32(&mtk_spm->spm_power_on_val0,
+ read32(&mtk_spm->pcm_reg0_data));
+ }
+
+ write32(&mtk_spm->pcm_pwr_io_en, 0);
+
+ clrsetbits_le32(&mtk_spm->pcm_con1,
+ PCM_TIMER_EN_LSB,
+ SPM_REGWR_CFG_KEY);
+
+ write32(&mtk_spm->pcm_con0, SPM_REGWR_CFG_KEY | PCM_CK_EN_LSB |
+ PCM_SW_RESET_LSB);
+ write32(&mtk_spm->pcm_con0, SPM_REGWR_CFG_KEY | PCM_CK_EN_LSB);
+
+ pcm_fsm_sta = read32(&mtk_spm->pcm_fsm_sta);
+
+ if ((pcm_fsm_sta & PCM_FSM_STA_MASK) != PCM_FSM_STA_DEF) {
+ printk(BIOS_ERR, "reset pcm(PCM_FSM_STA=%#x)\n",
+ read32(&mtk_spm->pcm_fsm_sta));
+ return -1;
+ }
+
+ write32(&mtk_spm->pcm_con0, SPM_REGWR_CFG_KEY | PCM_CK_EN_LSB |
+ EN_IM_SLEEP_DVS_LSB);
+
+ con1 = read32(&mtk_spm->pcm_con1) & PCM_WDT_WAKE_MODE_LSB;
+ write32(&mtk_spm->pcm_con1, con1 | SPM_REGWR_CFG_KEY |
+ EVENT_LOCK_EN_LSB | SPM_SRAM_ISOINT_B_LSB |
+ (pcmdesc->replace ? 0 : IM_NONRP_EN_LSB) |
+ MIF_APBEN_LSB | SCP_APB_INTERNAL_EN_LSB);
+
+ return 0;
+}
+
+static void spm_load_pcm_code(const struct dyna_load_pcm *pcm)
+{
+ int i;
+
+ write32(&mtk_spm->pcm_con1, read32(&mtk_spm->pcm_con1) |
+ SPM_REGWR_CFG_KEY | IM_SLAVE_LSB);
+
+ for (i = 0; i < pcm->desc.size; i++) {
+ write32(&mtk_spm->pcm_im_host_rw_ptr,
+ PCM_IM_HOST_EN_LSB | PCM_IM_HOST_W_EN_LSB | i);
+ write32(&mtk_spm->pcm_im_host_rw_dat,
+ (u32) *(pcm->buf + i));
+ }
+ write32(&mtk_spm->pcm_im_host_rw_ptr, 0);
+}
+
+static void spm_check_pcm_code(const struct dyna_load_pcm *pcm)
+{
+ int i;
+
+ for (i = 0; i < pcm->desc.size; i++) {
+ write32(&mtk_spm->pcm_im_host_rw_ptr, PCM_IM_HOST_EN_LSB | i);
+ if ((read32(&mtk_spm->pcm_im_host_rw_dat)) !=
+ (u32) *(pcm->buf + i))
+ spm_load_pcm_code(pcm);
+ }
+ write32(&mtk_spm->pcm_im_host_rw_ptr, 0);
+}
+
+static void spm_kick_im_to_fetch(const struct dyna_load_pcm *pcm)
+{
+ u32 con0;
+
+ spm_load_pcm_code(pcm);
+ spm_check_pcm_code(pcm);
+
+ printk(BIOS_DEBUG, "%s: ptr = %p\n", __func__, pcm->buf);
+ printk(BIOS_DEBUG, "%s: len = %d\n", __func__, pcm->desc.size);
+
+ con0 = read32(&mtk_spm->pcm_con0) & ~(IM_KICK_L_LSB | PCM_KICK_L_LSB);
+ write32(&mtk_spm->pcm_con0, con0 | SPM_REGWR_CFG_KEY |
+ PCM_CK_EN_LSB | IM_KICK_L_LSB);
+ write32(&mtk_spm->pcm_con0, con0 | SPM_REGWR_CFG_KEY | PCM_CK_EN_LSB);
+}
+
+static void spm_init_pcm_register(void)
+{
+ write32(&mtk_spm->pcm_reg_data_ini,
+ read32(&mtk_spm->spm_power_on_val0));
+ write32(&mtk_spm->pcm_pwr_io_en, PCM_RF_SYNC_R0);
+ write32(&mtk_spm->pcm_pwr_io_en, 0);
+
+ write32(&mtk_spm->pcm_reg_data_ini,
+ read32(&mtk_spm->spm_power_on_val1));
+ write32(&mtk_spm->pcm_pwr_io_en, PCM_RF_SYNC_R7);
+ write32(&mtk_spm->pcm_pwr_io_en, 0);
+}
+
+static void spm_init_event_vector(const struct pcm_desc *pcmdesc)
+{
+ for (int i = 0; i < PCM_EVENT_VECTOR_NUM; i++)
+ write32(&mtk_spm->pcm_event_vector[i], pcmdesc->vector[i]);
+}
+
+static const char * const dyna_load_pcm_path[] = {
+ [DYNA_LOAD_PCM_SUSPEND_LP4_3733] = "pcm_allinone_lp4_3733.bin",
+ [DYNA_LOAD_PCM_SUSPEND_LP4_3200] = "pcm_allinone_lp4_3200.bin",
+};
+
+static int spm_load_firmware(enum dyna_load_pcm_index index,
+ struct dyna_load_pcm *pcm)
+{
+ /*
+ * Layout:
+ * u16 firmware_size
+ * u32 binary[firmware_size]
+ * struct pcm_desc descriptor
+ * char *version
+ */
+ u16 firmware_size;
+ int copy_size;
+ const char *file_name = dyna_load_pcm_path[index];
+ struct stopwatch sw;
+
+ stopwatch_init(&sw);
+
+ size_t file_size = cbfs_boot_load_file(file_name, spm_bin,
+ sizeof(spm_bin), CBFS_TYPE_RAW);
+
+ if (file_size == 0) {
+ printk(BIOS_ERR, "SPM binary %s not found\n", file_name);
+ return -1;
+ }
+
+ int offset = 0;
+
+ /* firmware size */
+ copy_size = sizeof(firmware_size);
+ memcpy(&firmware_size, spm_bin + offset, copy_size);
+ printk(BIOS_DEBUG, "SPM: binary array size = %d\n", firmware_size);
+ offset += copy_size;
+
+ /* binary */
+ assert(offset < file_size);
+ copy_size = firmware_size * 4;
+ pcm->buf = (u32 *)(spm_bin + offset);
+ offset += copy_size;
+
+ /* descriptor */
+ assert(offset < file_size);
+ copy_size = sizeof(struct pcm_desc);
+ memcpy((void *)&(pcm->desc.size), spm_bin + offset, copy_size);
+ offset += copy_size;
+
+ /* version */
+ /* The termintating character should be contained in the spm binary */
+ assert(spm_bin[file_size - 1] == '\0');
+ assert(offset < file_size);
+ printk(BIOS_DEBUG, "SPM: version = %s\n", spm_bin + offset);
+
+ printk(BIOS_INFO, "SPM binary loaded in %ld msecs\n",
+ stopwatch_duration_msecs(&sw));
+
+ return 0;
+}
+
+static void spm_kick_pcm_to_run(void)
+{
+ uint32_t con0;
+
+ write32(&mtk_spm->spm_mas_pause_mask_b, SPM_MAS_PAUSE_MASK_B_VAL);
+ write32(&mtk_spm->spm_mas_pause2_mask_b, SPM_MAS_PAUSE2_MASK_B_VAL);
+ write32(&mtk_spm->pcm_reg_data_ini, 0);
+
+ write32(&mtk_spm->pcm_pwr_io_en, PCM_PWRIO_EN_R0 | PCM_PWRIO_EN_R7);
+
+ printk(BIOS_DEBUG, "SPM: %s\n", __func__);
+
+ /* check IM ready */
+ while ((read32(&mtk_spm->pcm_fsm_sta) & IM_STATE_MASK) != IM_STATE)
+ ;
+
+ /* kick PCM to run, and toggle PCM_KICK */
+ con0 = read32(&mtk_spm->pcm_con0) & ~(IM_KICK_L_LSB | PCM_KICK_L_LSB);
+ write32(&mtk_spm->pcm_con0, con0 | SPM_REGWR_CFG_KEY | PCM_CK_EN_LSB |
+ PCM_KICK_L_LSB);
+ write32(&mtk_spm->pcm_con0, con0 | SPM_REGWR_CFG_KEY | PCM_CK_EN_LSB);
+
+ printk(BIOS_DEBUG, "SPM: %s done\n", __func__);
+}
+
+int spm_init(void)
+{
+ struct pcm_desc *pcmdesc;
+ enum dyna_load_pcm_index index;
+ struct stopwatch sw;
+
+ stopwatch_init(&sw);
+
+ if (CONFIG(MT8183_DRAM_EMCP))
+ index = DYNA_LOAD_PCM_SUSPEND_LP4_3733;
+ else
+ index = DYNA_LOAD_PCM_SUSPEND_LP4_3200;
+
+ printk(BIOS_DEBUG, "SPM: pcm index = %d\n", index);
+
+ struct dyna_load_pcm pcm;
+ if (spm_load_firmware(index, &pcm)) {
+ printk(BIOS_ERR, "SPM: firmware is not ready\n");
+ printk(BIOS_ERR, "SPM: check dram type and firmware version\n");
+ return -1;
+ }
+
+ pcmdesc = &pcm.desc;
+
+ if (spm_register_init())
+ return -1;
+
+ if (spm_reset_and_init_pcm(pcmdesc))
+ return -1;
+
+ spm_kick_im_to_fetch(&pcm);
+ spm_init_pcm_register();
+ spm_init_event_vector(pcmdesc);
+ spm_kick_pcm_to_run();
+
+ printk(BIOS_INFO, "SPM: %s done in %ld msecs\n", __func__,
+ stopwatch_duration_msecs(&sw));
+
+ return 0;
+}