diff options
-rw-r--r-- | src/mainboard/google/kukui/mainboard.c | 4 | ||||
-rw-r--r-- | src/soc/mediatek/mt8183/Makefile.inc | 15 | ||||
-rw-r--r-- | src/soc/mediatek/mt8183/include/soc/spm.h | 186 | ||||
-rw-r--r-- | src/soc/mediatek/mt8183/spm.c | 351 |
4 files changed, 507 insertions, 49 deletions
diff --git a/src/mainboard/google/kukui/mainboard.c b/src/mainboard/google/kukui/mainboard.c index 7b00d94ba6..844496d7a0 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 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; +} |