summaryrefslogtreecommitdiff
path: root/src/soc/mediatek/mt8183
diff options
context:
space:
mode:
authorDawei Chien <dawei.chien@mediatek.com>2019-05-30 11:55:10 +0800
committerPatrick Georgi <pgeorgi@google.com>2019-09-30 11:38:42 +0000
commit2422f8c21e4590dcf75239583099a467f79f9878 (patch)
tree4034a79d2b5a531e89106c30a4420f6d69bf4fa6 /src/soc/mediatek/mt8183
parent45ffee83464156a58a57570b22ba35ab2859e532 (diff)
downloadcoreboot-2422f8c21e4590dcf75239583099a467f79f9878.tar.xz
mediatek/mt8183: Init SPM driver
To support mt8183 power saving during suspend to RAM, this patch loads SPM firmware to support SPM suspend. SPM needs its own firmware to do these power saving in the right timing under correct conditions. After linux PM suspends, SPM is able to turn off power for the last CPU and do more power saving for the SoC such as DRAM self-refresh mode and turning off 26M crystal. BUG=none BRANCH=none TEST=suspend/resume passes for LPDDR4 3200 Change-Id: I3393a772f025b0912a5a25a63a87512454fbc86e Signed-off-by: Dawei Chien <dawei.chien@mediatek.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/34545 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Yu-Ping Wu <yupingso@google.com> Reviewed-by: Hung-Te Lin <hungte@chromium.org>
Diffstat (limited to 'src/soc/mediatek/mt8183')
-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
3 files changed, 503 insertions, 49 deletions
diff --git a/src/soc/mediatek/mt8183/Makefile.inc b/src/soc/mediatek/mt8183/Makefile.inc
index b6c3a33819..d1171ef379 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 5e7770eecb..4ca72b632a 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 0000000000..669970fb2f
--- /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;
+}