/* * Copyright (c) 2022, MediaTek Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include "mt_cpu_pm.h" #include "mt_cpu_pm_cpc.h" #include "mt_smp.h" #include struct mtk_cpc_dev { int auto_off; unsigned int auto_thres_tick; }; static struct mtk_cpc_dev cpc; static int mtk_cpc_last_core_prot(int prot_req, int resp_reg, int resp_ofs) { unsigned int staus; unsigned int retry = 0; while (retry < RETRY_CNT_MAX) { retry++; mmio_write_32(CPC_MCUSYS_LAST_CORE_REQ, prot_req); udelay(1); staus = (mmio_read_32(resp_reg) >> resp_ofs) & CPC_PROT_RESP_MASK; if (staus == PROT_SUCCESS) { return CPC_SUCCESS; } else if (staus == PROT_GIVEUP) { return CPC_ERR_FAIL; } } return CPC_ERR_TIMEOUT; } static int mtk_cpu_pm_mcusys_prot_aquire(void) { return mtk_cpc_last_core_prot(MCUSYS_PROT_SET, CPC_MCUSYS_LAST_CORE_RESP, MCUSYS_RESP_OFS); } static void mtk_cpu_pm_mcusys_prot_release(void) { mmio_write_32(CPC_MCUSYS_PWR_ON_MASK, MCUSYS_PROT_CLR); } int mtk_cpu_pm_cluster_prot_aquire(void) { return mtk_cpc_last_core_prot(CPUSYS_PROT_SET, CPC_MCUSYS_MP_LAST_CORE_RESP, CPUSYS_RESP_OFS); } void mtk_cpu_pm_cluster_prot_release(void) { mmio_write_32(CPC_MCUSYS_PWR_ON_MASK, CPUSYS_PROT_CLR); } static void mtk_cpc_cluster_cnt_backup(void) { /* single cluster */ uint32_t backup_cnt = mmio_read_32(CPC_CLUSTER_CNT_BACKUP); uint32_t curr_cnt = mmio_read_32(CPC_MCUSYS_CLUSTER_COUNTER); if ((curr_cnt & 0x7fff) == 0) { curr_cnt = (curr_cnt >> 16) & 0x7fff; } else { curr_cnt = curr_cnt & 0x7fff; } mmio_write_32(CPC_CLUSTER_CNT_BACKUP, backup_cnt + curr_cnt); mmio_write_32(CPC_MCUSYS_CLUSTER_COUNTER_CLR, 0x3); } static inline void mtk_cpc_mcusys_off_enable(bool enable) { mmio_write_32(CPC_MCUSYS_PWR_CTRL, enable ? 1 : 0); } void mtk_cpc_mcusys_off_reflect(void) { mtk_cpc_mcusys_off_enable(false); mtk_cpu_pm_mcusys_prot_release(); } int mtk_cpc_mcusys_off_prepare(void) { if (mtk_cpu_pm_mcusys_prot_aquire() != CPC_SUCCESS) { return CPC_ERR_FAIL; } mtk_cpc_cluster_cnt_backup(); mtk_cpc_mcusys_off_enable(true); return CPC_SUCCESS; } void mtk_cpc_core_on_hint_set(int cpu) { mmio_write_32(CPC_MCUSYS_CPU_ON_SW_HINT_SET, BIT(cpu)); } void mtk_cpc_core_on_hint_clr(int cpu) { mmio_write_32(CPC_MCUSYS_CPU_ON_SW_HINT_CLR, BIT(cpu)); } static void mtk_cpc_dump_timestamp(void) { unsigned int id; for (id = 0; id < CPC_TRACE_ID_NUM; id++) { mmio_write_32(CPC_MCUSYS_TRACE_SEL, id); memcpy((void *)(uintptr_t)CPC_TRACE_SRAM(id), (const void *)(uintptr_t)CPC_MCUSYS_TRACE_DATA, CPC_TRACE_SIZE); } } void mtk_cpc_time_sync(void) { uint64_t kt; uint32_t systime_l, systime_h; kt = sched_clock(); systime_l = mmio_read_32(CNTSYS_L_REG); systime_h = mmio_read_32(CNTSYS_H_REG); /* sync kernel timer to cpc */ mmio_write_32(CPC_MCUSYS_CPC_KERNEL_TIME_L_BASE, (uint32_t)kt); mmio_write_32(CPC_MCUSYS_CPC_KERNEL_TIME_H_BASE, (uint32_t)(kt >> 32)); /* sync system timer to cpc */ mmio_write_32(CPC_MCUSYS_CPC_SYSTEM_TIME_L_BASE, systime_l); mmio_write_32(CPC_MCUSYS_CPC_SYSTEM_TIME_H_BASE, systime_h); } static void mtk_cpc_config(unsigned int cfg, unsigned int data) { switch (cfg) { case CPC_SMC_CONFIG_PROF: if (data) { mmio_setbits_32(CPC_MCUSYS_CPC_DBG_SETTING, CPC_PROF_EN); } else { mmio_clrbits_32(CPC_MCUSYS_CPC_DBG_SETTING, CPC_PROF_EN); } break; case CPC_SMC_CONFIG_AUTO_OFF: if (data) { mmio_setbits_32(CPC_MCUSYS_CPC_FLOW_CTRL_CFG, CPC_AUTO_OFF_EN); cpc.auto_off = 1; } else { mmio_setbits_32(CPC_MCUSYS_CPC_FLOW_CTRL_CFG, CPC_AUTO_OFF_EN); cpc.auto_off = 0; } break; case CPC_SMC_CONFIG_AUTO_OFF_THRES: cpc.auto_thres_tick = US_TO_TICKS(data); mmio_write_32(CPC_MCUSYS_CPC_OFF_THRES, cpc.auto_thres_tick); break; case CPC_SMC_CONFIG_CNT_CLR: mmio_write_32(CPC_MCUSYS_CLUSTER_COUNTER_CLR, 0x3); break; case CPC_SMC_CONFIG_TIME_SYNC: mtk_cpc_time_sync(); break; default: break; } } static unsigned int mtk_cpc_read_config(unsigned int cfg) { unsigned int res = 0; switch (cfg) { case CPC_SMC_CONFIG_PROF: res = (mmio_read_32(CPC_MCUSYS_CPC_DBG_SETTING) & CPC_PROF_EN) ? 1 : 0; break; case CPC_SMC_CONFIG_AUTO_OFF: res = cpc.auto_off; break; case CPC_SMC_CONFIG_AUTO_OFF_THRES: res = TICKS_TO_US(cpc.auto_thres_tick); break; case CPC_SMC_CONFIG_CNT_CLR: default: break; } return res; } uint64_t mtk_cpc_handler(uint64_t act, uint64_t arg1, uint64_t arg2) { uint64_t res = 0; switch (act) { case CPC_SMC_EVENT_CPC_CONFIG: mtk_cpc_config((unsigned int)arg1, (unsigned int)arg2); break; case CPC_SMC_EVENT_READ_CONFIG: res = mtk_cpc_read_config((unsigned int)arg1); break; case CPC_SMC_EVENT_GIC_DPG_SET: /* isolated_status = x2; */ default: break; } return res; } uint64_t mtk_cpc_trace_dump(uint64_t act, uint64_t arg1, uint64_t arg2) { switch (act) { case CPC_SMC_EVENT_DUMP_TRACE_DATA: mtk_cpc_dump_timestamp(); break; default: break; } return 0; } void mtk_cpc_init(void) { #if CONFIG_MTK_SMP_EN mt_smp_init(); #endif mmio_setbits_32(CPC_MCUSYS_CPC_DBG_SETTING, (CPC_DBG_EN | CPC_CALC_EN)); cpc.auto_off = 1; mmio_setbits_32(CPC_MCUSYS_CPC_FLOW_CTRL_CFG, (CPC_OFF_PRE_EN | ((cpc.auto_off > 0) ? CPC_AUTO_OFF_EN : 0))); mtk_cpc_config(CPC_SMC_CONFIG_AUTO_OFF_THRES, 8000); /* enable CPC */ mmio_setbits_32(CPC_MCUSYS_CPC_FLOW_CTRL_CFG, CPC_CTRL_ENABLE); mmio_setbits_32(CPC_MCUSYS_CPC_FLOW_CTRL_CFG, SSPM_CORE_PWR_ON_EN); }