784 lines
22 KiB
C
Executable file
784 lines
22 KiB
C
Executable file
/*
|
|
* copyright (c) 2021 Samsung Electronics Co., Ltd.
|
|
* http://www.samsung.com/
|
|
*
|
|
* EXYNOS - CryptoManager for SSS H/W debugging
|
|
* Author: Jiye Min <jiye.min@samsung.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/printk.h>
|
|
#include <linux/module.h>
|
|
#include <linux/device.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/kdebug.h>
|
|
#include <asm/cacheflush.h>
|
|
#include <asm/memory.h>
|
|
#include <asm/arch_timer.h>
|
|
#include <soc/samsung/exynos-smc.h>
|
|
#include <soc/samsung/exynos-cm.h>
|
|
#if IS_ENABLED(CONFIG_EXYNOS_SEH)
|
|
#include <soc/samsung/exynos-seh.h>
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_EXYNOS_ITMON)
|
|
#include <soc/samsung/exynos-itmon.h>
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_DEBUG_SNAPSHOT)
|
|
#include <soc/samsung/debug-snapshot.h>
|
|
#endif
|
|
|
|
#define CM_SSS_HWSEMA_NOT_SUPPORT (0x70800) /* define at LDFW */
|
|
#define CM_INVALID_SMC_CMD (0x10100) /* define at LDFW */
|
|
#define CM_TRUE (0x91827364)
|
|
|
|
#define CM_MAX_RETRY_COUNT (10000)
|
|
|
|
/* smc to call ldfw functions */
|
|
#define SMC_CM_SSS_HW_SEMA (0xC2001031)
|
|
#define CMD_GET_HW_SEMA_ENABLED (1)
|
|
#define CMD_GET_DEBUG_ADDR (2)
|
|
|
|
static struct cm_sss_debug_mem *debug_mem;
|
|
static void __iomem *sysreg_va_base;
|
|
static u32 flag_set_sss_debug_mem;
|
|
static u32 flag_enable_hw_sema;
|
|
static u64 flag_check_timestamp;
|
|
|
|
struct cm_device {
|
|
struct device *dev;
|
|
|
|
u32 sss_ap_sfr_addr;
|
|
u32 sss_cp_sfr_addr;
|
|
u32 sss_debug_sysreg_addr; /* HW_APBSEMA_MEC_DBG address */
|
|
|
|
u64 cm_debug_mem_addr;
|
|
u64 cm_debug_mem_va_addr;
|
|
u32 cm_debug_mem_size;
|
|
|
|
unsigned long sss_debug_mem_pa_addr;
|
|
|
|
#if IS_ENABLED(CONFIG_EXYNOS_ITMON)
|
|
struct notifier_block itmon_nb;
|
|
#endif
|
|
u32 itmon_notified;
|
|
#if IS_ENABLED(CONFIG_DEBUG_SNAPSHOT)
|
|
struct notifier_block panic_nb;
|
|
struct notifier_block die_nb;
|
|
#endif
|
|
u32 panic_notified;
|
|
};
|
|
|
|
static int exynos_cm_smc(u64 *arg0, u64 *arg1, u64 *arg2, u64 *arg3)
|
|
{
|
|
struct arm_smccc_res res;
|
|
|
|
arm_smccc_smc(*arg0, *arg1, *arg2, *arg3, 0, 0, 0, 0, &res);
|
|
|
|
*arg0 = res.a0;
|
|
*arg1 = res.a1;
|
|
*arg2 = res.a2;
|
|
*arg3 = res.a3;
|
|
|
|
return *arg0;
|
|
}
|
|
|
|
static int exynos_cm_map_sfr(u32 pa_addr, void __iomem **va_addr, unsigned int map_size)
|
|
{
|
|
int ret = 0;
|
|
|
|
*va_addr = ioremap(pa_addr, map_size);
|
|
if (!*va_addr) {
|
|
pr_err("%s: fail to ioremap\n", __func__);
|
|
ret = -EFAULT;
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int exynos_cm_get_hw_sema_enabled(u32 *info)
|
|
{
|
|
u64 reg0;
|
|
u64 reg1;
|
|
u64 reg2;
|
|
u64 reg3;
|
|
int ret;
|
|
|
|
reg0 = SMC_CM_SSS_HW_SEMA;
|
|
reg1 = CMD_GET_HW_SEMA_ENABLED;
|
|
reg2 = 0;
|
|
reg3 = 0;
|
|
|
|
ret = exynos_cm_smc(®0, ®1, ®2, ®3);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
*info = (uint32_t)reg2;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int exynos_cm_get_debug_info(u64 *addr, u32 *size)
|
|
{
|
|
u64 reg0;
|
|
u64 reg1;
|
|
u64 reg2;
|
|
u64 reg3;
|
|
int ret;
|
|
|
|
reg0 = SMC_CM_SSS_HW_SEMA;
|
|
reg1 = CMD_GET_DEBUG_ADDR;
|
|
reg2 = 0;
|
|
reg3 = 0;
|
|
|
|
ret = exynos_cm_smc(®0, ®1, ®2, ®3);
|
|
if (ret != 0) {
|
|
pr_err("%s: exynos_cm_get_debug_info failed: 0x%x\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
*addr = reg2;
|
|
*size = reg3;
|
|
|
|
return ret;
|
|
}
|
|
|
|
void exynos_cm_set_sss_debug_mem(void)
|
|
{
|
|
void __iomem *DBG_ADDR = sysreg_va_base;
|
|
struct cm_sss_debug_mem *info = debug_mem;
|
|
unsigned int DBG_VAL = 0;
|
|
unsigned int DBG_STAT_VAL = 0;
|
|
unsigned int DBG_REQ_CANCEL_CNT_AP = 0;
|
|
unsigned int DBG_REQ_CANCEL_CNT_CP = 0;
|
|
unsigned int DBG_REQ_PERIOD_CNT_AP = 0;
|
|
unsigned int DBG_REQ_PERIOD_CNT_CP = 0;
|
|
unsigned int DBG_TO_CNT_AP = 0;
|
|
unsigned int DBG_TO_CNT_CP = 0;
|
|
unsigned int temp, retry_cnt;
|
|
uint8_t ap_name[CM_MAGIC_STRING_LEN] = "AP_INFO";
|
|
uint8_t cp_name[CM_MAGIC_STRING_LEN] = "CP_INFO";
|
|
|
|
/* If SSS HW_SEMA is not enabled, return */
|
|
if (flag_enable_hw_sema != CM_TRUE)
|
|
return;
|
|
|
|
if (!DBG_ADDR || !info) {
|
|
pr_info("%s: DBG_ADDR %lx, info %lx\n", __func__, DBG_ADDR, info);
|
|
return;
|
|
}
|
|
|
|
if (flag_set_sss_debug_mem == CM_TRUE) {
|
|
pr_info("%s: already function is called\n", __func__);
|
|
return;
|
|
}
|
|
|
|
/* read debug register */
|
|
DBG_VAL = readl(DBG_ADDR);
|
|
DBG_STAT_VAL = readl(DBG_ADDR + SYSREG_OFFSET_DBG_STATE);
|
|
DBG_REQ_CANCEL_CNT_AP = readl(DBG_ADDR + SYSREG_OFFSET_DBG_REQ_CANCEL_CNT_AP);
|
|
DBG_REQ_CANCEL_CNT_CP = readl(DBG_ADDR+ SYSREG_OFFSET_DBG_REQ_CANCEL_CNT_CP);
|
|
DBG_REQ_PERIOD_CNT_AP = readl(DBG_ADDR+ SYSREG_OFFSET_DBG_REQ_PERIOD_CNT_AP);
|
|
DBG_REQ_PERIOD_CNT_CP = readl(DBG_ADDR + SYSREG_OFFSET_DBG_REQ_PERIOD_CNT_CP);
|
|
|
|
/* set AP debugging information */
|
|
memcpy(info->ap_info.magic, ap_name, sizeof(ap_name));
|
|
info->ap_info.dbg_req = ((DBG_VAL & HW_SEMA_MEC_DBG_REQ_MASK) &
|
|
HW_SEMA_MEC_DBG_REQ_AP) >> DBG_REQ_AP_SHIFT;
|
|
info->ap_info.dbg_ack = ((DBG_VAL & HW_SEMA_MEC_DBG_ACK_MASK) &
|
|
HW_SEMA_MEC_DBG_ACK_AP) >> DBG_ACK_AP_SHIFT;
|
|
|
|
/* interrupt status */
|
|
temp = DBG_VAL & HW_SEMA_MEC_DBG_INT_STATUS_MASK;
|
|
info->ap_info.dbg_ack_int_stat = (temp & HW_SEMA_MEC_DBG_ACK_INT_STATUS_AP) >>
|
|
DBG_ACK_INT_STAT_AP_SHIFT;
|
|
info->ap_info.dbg_to_int_stat = (temp & HW_SEMA_MEC_DBG_TO_INT_STATUS_AP) >>
|
|
DBG_TO_INT_STAT_AP_SHIFT;
|
|
|
|
/* interrupt history */
|
|
temp = DBG_VAL & HW_SEMA_MEC_DBG_INT_HISTORY_MASK;
|
|
info->ap_info.dbg_to_history = (temp & HW_SEMA_MEC_DBG_TO_HISTORY_AP) >>
|
|
DBG_TO_HISTORY_AP_SHIFT;
|
|
info->ap_info.dbg_ack_history = (temp & HW_SEMA_MEC_DBG_ACK_HISTORY_AP) >>
|
|
DBG_ACK_HISTORY_AP_SHIFT;
|
|
|
|
/* counter values */
|
|
info->ap_info.dbg_req_cancel_cnt = DBG_REQ_CANCEL_CNT_AP &
|
|
HW_SEMA_MEC_DBG_REQ_CANCLE_CNT_AP_MASK;
|
|
info->ap_info.dbg_req_period_cnt = DBG_REQ_PERIOD_CNT_AP &
|
|
HW_SEMA_MEC_DBG_REQ_PERIOD_CNT_AP_MASK;
|
|
|
|
/* timeout counter of AP */
|
|
retry_cnt = 0;
|
|
do {
|
|
if (retry_cnt++ > CM_MAX_RETRY_COUNT) {
|
|
pr_info("%s: exceed retry in read TIMEOUT_CNT_AP", __func__);
|
|
break;
|
|
}
|
|
|
|
DBG_TO_CNT_AP = readl(DBG_ADDR + SYSREG_OFFSET_DBG_TO_CNT_AP);
|
|
} while ((DBG_TO_CNT_AP & HW_SEMA_MEC_DBG_TO_CNT_AP_MASK) == 0);
|
|
|
|
info->ap_info.dbg_to_cnt = DBG_TO_CNT_AP & HW_SEMA_MEC_DBG_TO_CNT_AP_MASK;
|
|
|
|
/* set CP debugging information */
|
|
memcpy(info->cp_info.magic, cp_name, sizeof(cp_name));
|
|
info->cp_info.dbg_req = ((DBG_VAL & HW_SEMA_MEC_DBG_REQ_MASK) &
|
|
HW_SEMA_MEC_DBG_REQ_CP) >> DBG_REQ_CP_SHIFT;
|
|
info->cp_info.dbg_ack = ((DBG_VAL & HW_SEMA_MEC_DBG_ACK_MASK) &
|
|
HW_SEMA_MEC_DBG_ACK_CP) >> DBG_ACK_CP_SHIFT;
|
|
|
|
/* interrupt status */
|
|
temp = DBG_VAL & HW_SEMA_MEC_DBG_INT_STATUS_MASK;
|
|
info->cp_info.dbg_ack_int_stat = (temp & HW_SEMA_MEC_DBG_ACK_INT_STATUS_CP) >>
|
|
DBG_ACK_INT_STAT_CP_SHIFT;
|
|
info->cp_info.dbg_to_int_stat = (temp & HW_SEMA_MEC_DBG_TO_INT_STATUS_CP) >>
|
|
DBG_TO_INT_STAT_CP_SHIFT;
|
|
|
|
/* interrupt history */
|
|
temp = DBG_VAL & HW_SEMA_MEC_DBG_INT_HISTORY_MASK;
|
|
info->cp_info.dbg_to_history = (temp & HW_SEMA_MEC_DBG_TO_HISTORY_CP) >>
|
|
DBG_TO_HISTORY_CP_SHIFT;
|
|
info->cp_info.dbg_ack_history = (temp & HW_SEMA_MEC_DBG_ACK_HISTORY_CP) >>
|
|
DBG_ACK_HISTORY_CP_SHIFT;
|
|
|
|
/* counter values */
|
|
info->cp_info.dbg_req_cancel_cnt = DBG_REQ_CANCEL_CNT_CP &
|
|
HW_SEMA_MEC_DBG_REQ_CANCLE_CNT_CP_MASK;
|
|
info->cp_info.dbg_req_period_cnt = DBG_REQ_PERIOD_CNT_CP &
|
|
HW_SEMA_MEC_DBG_REQ_PERIOD_CNT_CP_MASK;
|
|
|
|
/* timeout counter of CP */
|
|
retry_cnt = 0;
|
|
do {
|
|
if (retry_cnt++ > CM_MAX_RETRY_COUNT) {
|
|
pr_info("%s: exceed retry in read TIMEOUT_CNT_CP", __func__);
|
|
break;
|
|
}
|
|
|
|
DBG_TO_CNT_CP = readl(DBG_ADDR + SYSREG_OFFSET_DBG_TO_CNT_CP);
|
|
} while ((DBG_TO_CNT_CP & HW_SEMA_MEC_DBG_TO_CNT_CP_MASK) == 0);
|
|
|
|
info->cp_info.dbg_to_cnt = DBG_TO_CNT_CP & HW_SEMA_MEC_DBG_TO_CNT_CP_MASK;
|
|
|
|
/* state information */
|
|
info->st_info.arbiter_state = (DBG_STAT_VAL & HW_SEMA_MEC_DBG_ARBITER_STAT_MASK) >>
|
|
DBQ_ARBITER_FSM_STAT_SHIFT;
|
|
|
|
/* core state */
|
|
temp = DBG_STAT_VAL & HW_SEMA_MEC_DBG_CORE_STAT_MASK;
|
|
info->st_info.core_state_ap = (temp & HW_SEMA_MEC_DBG_CORE_FSM_STAT_AP) >>
|
|
DBG_CORE_FSM_STAT_AP_SHIFT;
|
|
info->st_info.core_state_cp = (temp & HW_SEMA_MEC_DBG_CORE_FSM_STAT_CP) >>
|
|
DBG_CORE_FSM_STAT_CP_SHIFT;
|
|
|
|
/* AZ state */
|
|
temp = DBG_STAT_VAL & HW_SEMA_MEC_DBG_AZ_STAT_MASK;
|
|
info->st_info.az_fsm_state = (temp & HW_SEMA_MEC_DBG_AZ_FSM_STAT) >>
|
|
DBG_AZ_FSM_STAT_SHIFT;
|
|
info->st_info.az_req_ap = (temp & HW_SEMA_MEC_DBG_AZ_REQ_AP) >>
|
|
DBG_AZ_REQ_AP_SHIFT;
|
|
info->st_info.az_req_cp = (temp & HW_SEMA_MEC_DBG_AZ_REQ_CP) >>
|
|
DBG_AZ_REQ_CP_SHIFT;
|
|
|
|
/* flush debug memory */
|
|
flush_dcache_page((struct page *)info);
|
|
|
|
flag_set_sss_debug_mem = CM_TRUE;
|
|
|
|
pr_info("%s: dump success.\n", __func__);
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_EXYNOS_ITMON)
|
|
static void exynos_cm_print_debug_arbiter_state(uint64_t state)
|
|
{
|
|
switch (state) {
|
|
case ARBITER_ST_IDLE:
|
|
pr_info(" - APBITER State: 0x%x (SEMA Disable state)", state);
|
|
break;
|
|
case ARBITER_ST_INIT:
|
|
pr_info(" - APBITER State: 0x%x (Init Value)", state);
|
|
break;
|
|
case ARBITER_ST_CHECK:
|
|
pr_info(" - APBITER State: 0x%x (Check PERMISSION)", state);
|
|
break;
|
|
case ARBITER_ST_STAY:
|
|
pr_info(" - APBITER State: 0x%x (Maintain PERMISSION)", state);
|
|
break;
|
|
default:
|
|
pr_info(" - ARBITER State is invalid");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void exynos_cm_print_debug_core_state(uint32_t core_num,
|
|
uint8_t *name,
|
|
uint64_t state)
|
|
{
|
|
switch (state) {
|
|
case CORE_FSM_ST_IDLE:
|
|
pr_info(" > Core %d (%s) FSM State: 0x%x (SEMA Disable state)",
|
|
core_num, name, state);
|
|
break;
|
|
case CORE_FSM_ST_INIT:
|
|
pr_info(" > Core %d (%s) FSM State: 0x%x (Init value (SEMA enable))",
|
|
core_num, name, state);
|
|
break;
|
|
case CORE_FSM_ST_WAIT_SEMA:
|
|
pr_info(" > Core %d (%s) FSM State: 0x%x (REQ and wait PERMISSION)",
|
|
core_num, name, state);
|
|
break;
|
|
case CORE_FSM_ST_DECODE:
|
|
pr_info(" > Core %d (%s) FSM State: 0x%x (Maintain PERMISSION)",
|
|
core_num, name, state);
|
|
break;
|
|
case CORE_FSM_ST_DATA:
|
|
pr_info(" > Core %d (%s) FSM State: 0x%x (DATA in progress)",
|
|
core_num, name, state);
|
|
break;
|
|
case CORE_FSM_ST_DONE:
|
|
pr_info(" > Core %d (%s) FSM State: 0x%x (PERMISSION release)",
|
|
core_num, name, state);
|
|
break;
|
|
default:
|
|
pr_info(" > Core %d (%s) FSM State is Invalid");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void exynos_cm_print_debug_az_state(uint64_t state)
|
|
{
|
|
switch (state) {
|
|
case AZ_FSM_ST_IDLE:
|
|
pr_info(" > AZ FSM State: 0x%x (SEMA Disable state)", state);
|
|
break;
|
|
case AZ_FSM_ST_INIT:
|
|
pr_info(" > AZ FSM State: 0x%x (Init value)", state);
|
|
break;
|
|
case AZ_FSM_ST_END:
|
|
pr_info(" > AZ FSM State: 0x%x (Auto Zeroise done)", state);
|
|
break;
|
|
case AZ_FSM_ST_INT_SET:
|
|
pr_info(" > AZ FSM State: 0x%x (Interrupt setting)", state);
|
|
break;
|
|
case AZ_FSM_ST_IP_SEL:
|
|
pr_info(" > AZ FSM State: 0x%x (Auto Zeroise IP select)", state);
|
|
break;
|
|
case AZ_FSM_ST_START:
|
|
pr_info(" > AZ FSM State: 0x%x (Auto Zeroise start)", state);
|
|
break;
|
|
case AZ_FSM_ST_WAIT:
|
|
pr_info(" > AZ FSM State: 0x%x (Waiting for Zeroise result)", state);
|
|
break;
|
|
case AZ_FSM_ST_DONE:
|
|
pr_info(" > AZ FSM State: 0x%x (Receive Zeroise done interrupt and clearing interrupt)",
|
|
state);
|
|
break;
|
|
case AZ_FSM_ST_ERROR:
|
|
pr_info(" > AZ FSM State: 0x%x (Receive Zeroise error interrupt and clearing interrupt)",
|
|
state);
|
|
break;
|
|
case AZ_FSM_ST_SW_RESET:
|
|
pr_info(" > AZ FSM State: 0x%x (S/W reset upon receiving Zeroise error interrupt",
|
|
state);
|
|
break;
|
|
case AZ_FSM_ST_SW_RESET_WAIT:
|
|
pr_info(" > AZ FSM State: 0x%x (Waiting for S/W reset result)", state);
|
|
break;
|
|
case AZ_FSM_ST_SW_RESET_CNT_INIT:
|
|
pr_info(" > AZ FSM State: 0x%x (S/W reset Period counter initialize)",
|
|
state);
|
|
break;
|
|
case AZ_FSM_ST_PKE_INT_SET:
|
|
pr_info(" > AZ FSM State: 0x%x (interrupt setting for PKE Zeroise)",
|
|
state);
|
|
break;
|
|
case AZ_FSM_ST_PKE_SEL:
|
|
pr_info(" > AZ FSM State: 0x%x (Auto Zeroise PKE select)", state);
|
|
break;
|
|
default:
|
|
pr_info(" > AZ FSM State is invalid");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void exynos_cm_print_sss_debug_sysreg(struct cm_device *cmdev)
|
|
{
|
|
struct cm_sss_debug_mem *info = debug_mem;
|
|
uint8_t ap_magic[4] = "AP";
|
|
uint8_t cp_magic[4] = "CP";
|
|
|
|
if (!info)
|
|
return;
|
|
|
|
pr_info("=========================[SSS ITMON ERROR]=========================");
|
|
pr_info(" > AP Information");
|
|
|
|
pr_info(" - REQ status: 0x%x", info->ap_info.dbg_req);
|
|
pr_info(" - ACK status: 0x%x", info->ap_info.dbg_ack);
|
|
|
|
pr_info(" - Interrupt Status");
|
|
pr_info(" > ACK Interrupt: 0x%x", info->ap_info.dbg_ack_int_stat);
|
|
if (info->ap_info.dbg_to_int_stat == 0) {
|
|
pr_info(" > Timeout Interrupt: 0x0");
|
|
} else {
|
|
pr_info(" > Timeout Interrupt: 0x1 (AP timeout is occured!)");
|
|
}
|
|
|
|
pr_info(" - Interrupt History (Indicates the previous usage history of HW_SEMA)");
|
|
if (info->ap_info.dbg_to_history == 0) {
|
|
pr_info(" > Timeout History: 0x0");
|
|
} else {
|
|
pr_info(" > Timeout History: 0x1 (AP timeout is occured!)");
|
|
}
|
|
|
|
if (info->ap_info.dbg_ack_history == 0) {
|
|
pr_info(" > SEMA_ACK History: 0x0");
|
|
} else {
|
|
pr_info(" > SEMA_ACK History: 0x1");
|
|
}
|
|
|
|
pr_info(" - Request Cancel Counter: 0x%x", info->ap_info.dbg_req_cancel_cnt);
|
|
pr_info(" - Request Period Counter: 0x%x", info->ap_info.dbg_req_period_cnt);
|
|
pr_info(" - Timeout Counter: 0x%x", info->ap_info.dbg_to_cnt);
|
|
|
|
pr_info("------------------------------------------------------------------");
|
|
pr_info(" > CP Information");
|
|
|
|
pr_info(" - REQ status: 0x%x", info->cp_info.dbg_req);
|
|
pr_info(" - ACK status: 0x%x", info->cp_info.dbg_ack);
|
|
|
|
pr_info(" - Interrupt Status");
|
|
pr_info(" > ACK Interrupt: 0x%x", info->cp_info.dbg_ack_int_stat);
|
|
if (info->cp_info.dbg_to_int_stat == 0) {
|
|
pr_info(" > Timeout Interrupt: 0x0");
|
|
} else {
|
|
pr_info(" > Timeout Interrupt: 0x1 (CP timeout is occured!)");
|
|
}
|
|
|
|
pr_info(" - Interrupt History (Indicates the previous usage history of HW_SEMA)");
|
|
if (info->cp_info.dbg_to_history == 0) {
|
|
pr_info(" > Timeout History: 0x0");
|
|
} else {
|
|
pr_info(" > Timeout History: 0x1 (CP timeout is occured!)");
|
|
}
|
|
|
|
if (info->cp_info.dbg_ack_history == 0) {
|
|
pr_info(" > SEMA_ACK History: 0x0");
|
|
} else {
|
|
pr_info(" > SEMA_ACK History: 0x1");
|
|
}
|
|
|
|
pr_info(" - Request Cancel Counter: 0x%x", info->cp_info.dbg_req_cancel_cnt);
|
|
pr_info(" - Request Period Counter: 0x%x", info->cp_info.dbg_req_period_cnt);
|
|
pr_info(" - Timeout Counter: 0x%x", info->cp_info.dbg_to_cnt);
|
|
|
|
pr_info("-----------------------------------------------------------------");
|
|
pr_info(" > State Information");
|
|
|
|
/* print ARBITER state */
|
|
exynos_cm_print_debug_arbiter_state(info->st_info.arbiter_state);
|
|
|
|
pr_info(" - Core State");
|
|
exynos_cm_print_debug_core_state(0, ap_magic, info->st_info.core_state_ap);
|
|
exynos_cm_print_debug_core_state(1, cp_magic, info->st_info.core_state_cp);
|
|
|
|
pr_info(" - AZ State");
|
|
exynos_cm_print_debug_az_state(info->st_info.az_fsm_state);
|
|
pr_info(" > AZ REQ AP: %d\n", info->st_info.az_req_ap);
|
|
pr_info(" > AZ REQ CP: %d\n", info->st_info.az_req_cp);
|
|
pr_info("=================================================================");
|
|
}
|
|
|
|
static int __exynos_cm_itmon_notifier(struct notifier_block *nb,
|
|
unsigned long action,
|
|
void *nb_data)
|
|
{
|
|
struct cm_device *cmdev = NULL;
|
|
struct itmon_notifier *itmon_info = nb_data;
|
|
int is_sss_itmon = 0;
|
|
|
|
/* check if SSS HW_SEMA is enabled */
|
|
if (flag_enable_hw_sema != CM_TRUE)
|
|
return 0;
|
|
|
|
if (flag_check_timestamp == 0) {
|
|
pr_info("%s: debug handler timestamp 0x%lx\n", __func__, arch_timer_read_cntpct_el0());
|
|
flag_check_timestamp = 1;
|
|
}
|
|
|
|
cmdev = container_of(nb, struct cm_device, itmon_nb);
|
|
|
|
if (cmdev->itmon_notified == CM_TRUE)
|
|
return NOTIFY_DONE;
|
|
|
|
if (IS_ERR_OR_NULL(itmon_info))
|
|
return NOTIFY_DONE;
|
|
|
|
/* check SSS ITMON error is occured */
|
|
if (itmon_info->port &&
|
|
(((itmon_info->target_addr & 0xFFFF0000) == cmdev->sss_ap_sfr_addr) ||
|
|
((itmon_info->target_addr & 0xFFFF0000) == cmdev->sss_cp_sfr_addr)))
|
|
is_sss_itmon = 1;
|
|
|
|
if (!is_sss_itmon)
|
|
return NOTIFY_DONE;
|
|
|
|
pr_info("%s: port: %s, dest: %s, master: %s\n", __func__,
|
|
itmon_info->port, itmon_info->dest, itmon_info->master);
|
|
|
|
/* print debugging information */
|
|
exynos_cm_set_sss_debug_mem();
|
|
exynos_cm_print_sss_debug_sysreg(cmdev);
|
|
|
|
cmdev->itmon_notified = CM_TRUE;
|
|
|
|
return NOTIFY_OK;
|
|
}
|
|
#endif
|
|
|
|
static int exynos_cm_debug_handler(struct cm_device *cmdev)
|
|
{
|
|
/* If SSS HW_SEMA is not enabled, return */
|
|
if (flag_enable_hw_sema != CM_TRUE)
|
|
return 0;
|
|
|
|
if (cmdev->panic_notified == CM_TRUE)
|
|
return 0;
|
|
|
|
if (flag_check_timestamp == 0) {
|
|
pr_info("%s: debug handler timestamp 0x%lx\n", __func__, arch_timer_read_cntpct_el0());
|
|
flag_check_timestamp = 1;
|
|
}
|
|
|
|
/* set SSS debugging information */
|
|
exynos_cm_set_sss_debug_mem();
|
|
|
|
cmdev->panic_notified = CM_TRUE;
|
|
|
|
pr_debug("%s: exynos_cm_debug_handler exit\n", __func__);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int exynos_cm_panic_handler(struct notifier_block *nb, unsigned long l, void *buf)
|
|
{
|
|
struct cm_device *cmdev = container_of(nb, struct cm_device, panic_nb);
|
|
|
|
pr_debug("%s called\n", __func__);
|
|
|
|
return exynos_cm_debug_handler(cmdev);
|
|
}
|
|
|
|
static int exynos_cm_die_handler(struct notifier_block *nb, unsigned long l, void *buf)
|
|
{
|
|
struct cm_device *cmdev = container_of(nb, struct cm_device, die_nb);
|
|
|
|
pr_debug("%s called\n", __func__);
|
|
|
|
return exynos_cm_debug_handler(cmdev);
|
|
}
|
|
|
|
static int exynos_cm_probe(struct platform_device *pdev)
|
|
{
|
|
struct cm_device *cmdev = NULL;
|
|
struct page *page_ptr;
|
|
struct device_node *np = NULL;
|
|
int ret;
|
|
#if IS_ENABLED(CONFIG_DEBUG_SNAPSHOT)
|
|
unsigned long log_base, log_size;
|
|
#endif
|
|
/* check if SSS HW_SEMA is enabled */
|
|
ret = exynos_cm_get_hw_sema_enabled(&flag_enable_hw_sema);
|
|
if (ret == CM_SSS_HWSEMA_NOT_SUPPORT || ret == CM_INVALID_SMC_CMD) {
|
|
pr_info("%s: SSS HW_SEMA is not support.\n", __func__);
|
|
flag_enable_hw_sema = 0;
|
|
return 0;
|
|
} else if (ret != 0) {
|
|
pr_err("%s: get status of SSS HW_SEMA is failed.\n", __func__);
|
|
flag_enable_hw_sema = 0;
|
|
return ret;
|
|
} else if (flag_enable_hw_sema == 0) {
|
|
pr_info("%s: SSS HW_SEMA is not enabled.\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
/* set flag */
|
|
flag_enable_hw_sema = CM_TRUE;
|
|
flag_check_timestamp = 0;
|
|
|
|
cmdev = devm_kzalloc(&pdev->dev, sizeof(*cmdev), GFP_KERNEL);
|
|
if (!cmdev) {
|
|
pr_err("%s: fail to devm_zalloc()\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
platform_set_drvdata(pdev, cmdev);
|
|
|
|
/* initialize cmdev struct */
|
|
cmdev->dev = &pdev->dev;
|
|
cmdev->cm_debug_mem_addr = 0;
|
|
cmdev->cm_debug_mem_va_addr = 0;
|
|
cmdev->itmon_notified = 0;
|
|
cmdev->panic_notified = 0;
|
|
|
|
/* set cm_sss_debug_mem */
|
|
page_ptr = alloc_pages(GFP_KERNEL, 1);
|
|
if (!page_ptr) {
|
|
pr_err("%s: alloc_page for cm_sss_debug failed.\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
debug_mem = (struct cm_sss_debug_mem *)page_address(page_ptr);
|
|
if (!debug_mem) {
|
|
pr_err("%s: kmalloc for cm_sss_debug failed.\n", __func__);
|
|
ret = -ENOMEM;
|
|
return ret;
|
|
}
|
|
|
|
/* to map & flush memory */
|
|
memset(debug_mem, 0x00, CM_SSS_DEBUG_MEM_SIZE);
|
|
memcpy(debug_mem->magic, cm_name, sizeof(cm_name));
|
|
flush_dcache_page((struct page *)debug_mem);
|
|
cmdev->sss_debug_mem_pa_addr = (unsigned long)virt_to_phys(debug_mem);
|
|
pr_info("%s: debug memory address: 0x%lx\n", __func__, cmdev->sss_debug_mem_pa_addr);
|
|
|
|
/* get information from device tree */
|
|
np = of_find_compatible_node(NULL, NULL, "samsung,exynos-cm");
|
|
if (np == NULL) {
|
|
pr_err("%s: Do not support exynos-cm\n", __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "sysreg_addr", &cmdev->sss_debug_sysreg_addr);
|
|
if (ret) {
|
|
pr_err("%s: Fail to get debug_sysreg_addr from device tree\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "ap_addr", &cmdev->sss_ap_sfr_addr);
|
|
if (ret) {
|
|
pr_err("%s: Fail to get ap_addr from device tree\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "cp_addr", &cmdev->sss_cp_sfr_addr);
|
|
if (ret) {
|
|
pr_err("%s: Fail to get cp_addr from device tree\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
/* get address & size for debugging to CryptoManager */
|
|
ret = exynos_cm_get_debug_info(&cmdev->cm_debug_mem_addr,
|
|
&cmdev->cm_debug_mem_size);
|
|
if (ret != 0) {
|
|
pr_err("%s: getting debug information is failed.\n", __func__);
|
|
return ret;
|
|
}
|
|
pr_info("%s: cm_debug_mem_addr: 0x%lx, size: 0x%x\n", __func__,
|
|
cmdev->cm_debug_mem_addr, cmdev->cm_debug_mem_size);
|
|
|
|
/* get VA addresses */
|
|
if (cmdev->sss_debug_sysreg_addr)
|
|
exynos_cm_map_sfr(cmdev->sss_debug_sysreg_addr, &sysreg_va_base, SZ_32);
|
|
|
|
if (cmdev->cm_debug_mem_addr)
|
|
cmdev->cm_debug_mem_va_addr = (u64)phys_to_virt(cmdev->cm_debug_mem_addr);
|
|
|
|
/* register notifiers */
|
|
cmdev->panic_nb.notifier_call = exynos_cm_panic_handler;
|
|
atomic_notifier_chain_register(&panic_notifier_list, &cmdev->panic_nb);
|
|
|
|
cmdev->die_nb.notifier_call = exynos_cm_die_handler;
|
|
register_die_notifier(&cmdev->die_nb);
|
|
#if IS_ENABLED(CONFIG_EXYNOS_SEH)
|
|
exynos_seh_set_cm_debug_function((unsigned long)exynos_cm_set_sss_debug_mem);
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_EXYNOS_ITMON)
|
|
cmdev->itmon_nb.notifier_call = __exynos_cm_itmon_notifier;
|
|
itmon_notifier_chain_register(&cmdev->itmon_nb);
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_DEBUG_SNAPSHOT)
|
|
log_base = (unsigned long)cmdev->sss_debug_mem_pa_addr;
|
|
log_size = CM_SSS_DEBUG_MEM_SIZE;
|
|
|
|
if (log_base != 0) {
|
|
ret = dbg_snapshot_add_bl_item_info(CM_LOG_DSS_NAME1,
|
|
log_base, log_size);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "Fail to add CM log dump - ret[%d]\n", ret);
|
|
}
|
|
}
|
|
|
|
log_base = (unsigned long)cmdev->cm_debug_mem_addr;
|
|
log_size = cmdev->cm_debug_mem_size;
|
|
|
|
if (log_base != 0 && log_size != 0) {
|
|
ret = dbg_snapshot_add_bl_item_info(CM_LOG_DSS_NAME2,
|
|
log_base, log_size);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "Fail to add CM log dump - ret[%d]\n", ret);
|
|
}
|
|
}
|
|
#endif
|
|
pr_info("%s: exynos-cm registered.\n", __func__);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int exynos_cm_remove(struct platform_device *pdev)
|
|
{
|
|
/* Do nothing */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id exynos_cm_of_match_table[] = {
|
|
{ .compatible = "samsung,exynos-cm", },
|
|
{ },
|
|
};
|
|
MODULE_DEVICE_TABLE(of, exynos_cm_of_match_table);
|
|
|
|
static struct platform_driver exynos_cm_driver = {
|
|
.probe = exynos_cm_probe,
|
|
.remove = exynos_cm_remove,
|
|
.driver = {
|
|
.name = "exynos-cm",
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = of_match_ptr(exynos_cm_of_match_table),
|
|
},
|
|
};
|
|
|
|
static int __init exynos_cm_init(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = platform_driver_register(&exynos_cm_driver);
|
|
if (ret)
|
|
return ret;
|
|
|
|
pr_info("%s: Exynos CM driver init done. \n", __func__);
|
|
pr_info("%s: Exynos CM driver, (c) 2021 Samsung Electronics\n", __func__);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __exit exynos_cm_exit(void)
|
|
{
|
|
platform_driver_unregister(&exynos_cm_driver);
|
|
}
|
|
|
|
module_init(exynos_cm_init);
|
|
module_exit(exynos_cm_exit);
|
|
|
|
MODULE_SOFTDEP("pre: exynos-seh");
|
|
MODULE_DESCRIPTION("Exynos CryptoManager driver");
|
|
MODULE_AUTHOR("<jiye.min@samsung.com>");
|
|
MODULE_LICENSE("GPL");
|
|
|