mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-07 08:48:05 -04:00
816 lines
21 KiB
C
816 lines
21 KiB
C
/*
|
|
* linux/arch/arm/mach-exynos/exynos-coresight.c
|
|
*
|
|
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
|
|
* http://www.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/init.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/types.h>
|
|
#include <linux/device.h>
|
|
#include <linux/module.h>
|
|
#include <linux/cpu.h>
|
|
#include <linux/cpu_pm.h>
|
|
#include <linux/smp.h>
|
|
#include <linux/of.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/suspend.h>
|
|
#include <linux/smpboot.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/exynos-ss.h>
|
|
|
|
#include <asm/core_regs.h>
|
|
#include "coresight-priv.h"
|
|
|
|
#define CHANNEL (0)
|
|
#define PORT (1)
|
|
#define NONE (-1)
|
|
#define ARR_SZ (2)
|
|
|
|
#define etm_writel(base, val, off) __raw_writel((val), base + off)
|
|
#define etm_readl(base, off) __raw_readl(base + off)
|
|
|
|
#define SOFT_LOCK(base) \
|
|
do { mb(); isb(); etm_writel(base, 0x0, LAR); } while (0)
|
|
|
|
#define SOFT_UNLOCK(base) \
|
|
do { etm_writel(base, OSLOCK_MAGIC, LAR); mb(); isb(); } while (0)
|
|
|
|
struct cpu_etm_info {
|
|
void __iomem *base;
|
|
u32 enabled;
|
|
u32 f_port[ARR_SZ];
|
|
};
|
|
|
|
struct funnel_info {
|
|
void __iomem *base;
|
|
u32 port_status;
|
|
u32 f_port[ARR_SZ];
|
|
};
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_ETF
|
|
struct etf_info {
|
|
void __iomem *base;
|
|
u32 f_port[ARR_SZ];
|
|
};
|
|
#endif
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_ETR
|
|
struct etr_info {
|
|
void __iomem *base;
|
|
struct clk *etr_clk;
|
|
u32 enabled;
|
|
u32 buf_addr;
|
|
u32 buf_size;
|
|
u32 buf_pointer;
|
|
};
|
|
#endif
|
|
struct exynos_trace_info {
|
|
struct cpu_etm_info cpu[NR_CPUS];
|
|
spinlock_t trace_lock;
|
|
u32 enabled;
|
|
|
|
u32 procsel;
|
|
u32 config;
|
|
u32 sync_period;
|
|
u32 victlr;
|
|
|
|
u32 funnel_num;
|
|
struct funnel_info *funnel;
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_ETF
|
|
u32 etf_num;
|
|
struct etf_info *etf;
|
|
#endif
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_ETR
|
|
struct etr_info etr;
|
|
#endif
|
|
};
|
|
|
|
static struct exynos_trace_info *g_trace_info;
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_ETB
|
|
static void __iomem *g_etb_base;
|
|
#endif
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_STM
|
|
static u32 stm_funnel_port;
|
|
#endif
|
|
static DEFINE_PER_CPU(struct task_struct *, etm_task);
|
|
|
|
static void exynos_funnel_init(void)
|
|
{
|
|
unsigned int i, port, channel;
|
|
struct funnel_info *funnel;
|
|
|
|
for (i = 0; i < g_trace_info->funnel_num; i++) {
|
|
funnel = &g_trace_info->funnel[i];
|
|
spin_lock(&g_trace_info->trace_lock);
|
|
SOFT_UNLOCK(funnel->base);
|
|
funnel->port_status = etm_readl(funnel->base, FUNCTRL);
|
|
funnel->port_status = (funnel->port_status & 0x3ff) | 0x300;
|
|
etm_writel(funnel->base, funnel->port_status, FUNCTRL);
|
|
etm_writel(funnel->base, 0x0, FUNPRIORCTRL);
|
|
SOFT_LOCK(funnel->base);
|
|
spin_unlock(&g_trace_info->trace_lock);
|
|
|
|
if (funnel->f_port[CHANNEL] != NONE) {
|
|
channel = funnel->f_port[CHANNEL];
|
|
port = funnel->f_port[PORT];
|
|
funnel = &g_trace_info->funnel[channel];
|
|
|
|
spin_lock(&g_trace_info->trace_lock);
|
|
SOFT_UNLOCK(funnel->base);
|
|
funnel->port_status = etm_readl(funnel->base, FUNCTRL);
|
|
funnel->port_status |= BIT(port);
|
|
etm_writel(funnel->base, funnel->port_status, FUNCTRL);
|
|
SOFT_LOCK(funnel->base);
|
|
spin_unlock(&g_trace_info->trace_lock);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void exynos_funnel_close(void)
|
|
{
|
|
unsigned int i;
|
|
struct funnel_info *funnel;
|
|
|
|
for (i = 0; i < g_trace_info->funnel_num; i++) {
|
|
funnel = &g_trace_info->funnel[i];
|
|
spin_lock(&g_trace_info->trace_lock);
|
|
SOFT_UNLOCK(funnel->base);
|
|
etm_writel(funnel->base, 0x300, FUNCTRL);
|
|
SOFT_LOCK(funnel->base);
|
|
spin_unlock(&g_trace_info->trace_lock);
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_ETF
|
|
static void exynos_etf_enable(void)
|
|
{
|
|
unsigned int i, port, channel;
|
|
struct etf_info *etf;
|
|
struct funnel_info *funnel;
|
|
|
|
for (i = 0; i < g_trace_info->etf_num; i++) {
|
|
etf = &g_trace_info->etf[i];
|
|
SOFT_UNLOCK(etf->base);
|
|
etm_writel(etf->base, 0x0, TMCCTL);
|
|
etm_writel(etf->base, 0x800, TMCRSZ);
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_ETR
|
|
etm_writel(etf->base, 0x2, TMCMODE);
|
|
#else
|
|
etm_writel(etf->base, 0x0, TMCMODE);
|
|
#endif
|
|
etm_writel(etf->base, 0x0, TMCTGR);
|
|
etm_writel(etf->base, 0x0, TMCFFCR);
|
|
etm_writel(etf->base, 0x1, TMCCTL);
|
|
SOFT_LOCK(etf->base);
|
|
|
|
if (etf->f_port[CHANNEL] != NONE) {
|
|
channel = etf->f_port[CHANNEL];
|
|
port = etf->f_port[PORT];
|
|
funnel = &g_trace_info->funnel[channel];
|
|
|
|
spin_lock(&g_trace_info->trace_lock);
|
|
SOFT_UNLOCK(funnel->base);
|
|
funnel->port_status = etm_readl(funnel->base, FUNCTRL);
|
|
funnel->port_status |= BIT(port);
|
|
etm_writel(funnel->base, funnel->port_status, FUNCTRL);
|
|
SOFT_LOCK(funnel->base);
|
|
spin_unlock(&g_trace_info->trace_lock);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void exynos_etf_disable(void)
|
|
{
|
|
unsigned int i, port, channel;
|
|
struct etf_info *etf;
|
|
struct funnel_info *funnel;
|
|
|
|
for (i = 0; i < g_trace_info->etf_num; i++) {
|
|
etf = &g_trace_info->etf[i];
|
|
SOFT_UNLOCK(etf->base);
|
|
etm_writel(etf->base, 0x0, TMCCTL);
|
|
SOFT_LOCK(etf->base);
|
|
|
|
if (etf->f_port[CHANNEL] != NONE) {
|
|
channel = etf->f_port[CHANNEL];
|
|
port = etf->f_port[PORT];
|
|
funnel = &g_trace_info->funnel[channel];
|
|
|
|
spin_lock(&g_trace_info->trace_lock);
|
|
SOFT_UNLOCK(funnel->base);
|
|
funnel->port_status = etm_readl(funnel->base, FUNCTRL);
|
|
funnel->port_status &= ~BIT(port);
|
|
etm_writel(funnel->base, funnel->port_status, FUNCTRL);
|
|
SOFT_LOCK(funnel->base);
|
|
spin_unlock(&g_trace_info->trace_lock);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_ETR
|
|
static void exynos_etr_enable(void)
|
|
{
|
|
struct etr_info *etr = &g_trace_info->etr;
|
|
|
|
SOFT_UNLOCK(etr->base);
|
|
etm_writel(etr->base, 0x0, TMCCTL);
|
|
etm_writel(etr->base, etr->buf_size, TMCRSZ);
|
|
etm_writel(etr->base, 0x4, TMCTGR);
|
|
etm_writel(etr->base, 0x0, TMCAXICTL);
|
|
etm_writel(etr->base, etr->buf_addr, TMCDBALO);
|
|
etm_writel(etr->base, 0x0, TMCDBAHI);
|
|
etm_writel(etr->base, etr->buf_pointer, TMCRWP);
|
|
etm_writel(etr->base, 0x0, TMCMODE);
|
|
etm_writel(etr->base, 0x2001, TMCFFCR);
|
|
etm_writel(etr->base, 0x1, TMCCTL);
|
|
SOFT_LOCK(etr->base);
|
|
}
|
|
|
|
static void exynos_etr_disable(void)
|
|
{
|
|
struct etr_info *etr = &g_trace_info->etr;
|
|
|
|
SOFT_UNLOCK(etr->base);
|
|
etm_writel(etr->base, 0x0, TMCCTL);
|
|
etr->buf_pointer = etm_readl(etr->base, TMCRWP);
|
|
SOFT_LOCK(etr->base);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_ETB
|
|
static void exynos_etb_enable(void __iomem *etb_base, int src)
|
|
{
|
|
int i;
|
|
unsigned int depth = etm_readl(etb_base, TMCRSZ);
|
|
|
|
SOFT_UNLOCK(etb_base);
|
|
etm_writel(etb_base, 0x0, TMCCTL);
|
|
etm_writel(etb_base, 0x0, TMCRWP);
|
|
|
|
/* clear entire RAM buffer */
|
|
for (i = 0; i < depth; i++)
|
|
etm_writel(etb_base, 0x0, TMCRWD);
|
|
|
|
/* reset write RAM pointer address */
|
|
etm_writel(etb_base, 0x0, TMCRWP);
|
|
/* reset read RAM pointer address */
|
|
etm_writel(etb_base, 0x0, TMCRRP);
|
|
etm_writel(etb_base, 0x1, TMCTGR);
|
|
|
|
if (src) {
|
|
etm_writel(etb_base, 0x0, TMCFFCR);
|
|
pr_info("Data formatter disabled!\n");
|
|
} else {
|
|
etm_writel(etb_base, 0x2001, TMCFFCR);
|
|
pr_info("Data formatter enabled!\n");
|
|
}
|
|
|
|
/* ETB trace capture enable */
|
|
etm_writel(etb_base, 0x1, TMCCTL);
|
|
SOFT_LOCK(etb_base);
|
|
}
|
|
|
|
static void exynos_etb_disable(void __iomem *etb_base, int src)
|
|
{
|
|
uint32_t ffcr;
|
|
|
|
SOFT_UNLOCK(etb_base);
|
|
if (src) {
|
|
etm_writel(etb_base, 0x2001, TMCFFCR);
|
|
pr_info("Data formatter enabled!\n");
|
|
} else {
|
|
etm_writel(etb_base, 0x0, TMCFFCR);
|
|
pr_info("Data formatter disabled!\n");
|
|
}
|
|
|
|
ffcr = etm_readl(etb_base, TMCFFCR);
|
|
ffcr |= BIT(6);
|
|
etm_writel(etb_base, ffcr, TMCFFCR);
|
|
|
|
udelay(1500);
|
|
etm_writel(etb_base, 0x0, TMCCTL);
|
|
|
|
udelay(1500);
|
|
SOFT_LOCK(etb_base);
|
|
}
|
|
|
|
extern void exynos_etb_etm(void)
|
|
{
|
|
struct funnel_info *funnel;
|
|
unsigned int channel, port;
|
|
|
|
exynos_etb_disable(g_etb_base, 0);
|
|
|
|
if (stm_funnel_port[CHANNEL] != NONE) {
|
|
channel = stm_funnel_port[CHANNEL];
|
|
port = stm_funnel_port[PORT];
|
|
funnel = &g_trace_info->funnel[channel];
|
|
|
|
spin_lock(&g_trace_info->trace_lock);
|
|
SOFT_UNLOCK(funnel->base);
|
|
funnel->port_status = etm_readl(funnel->base, FUNCTRL);
|
|
funnel->port_status &= ~BIT(port);
|
|
etm_writel(funnel->base, funnel->port_status, FUNCTRL);
|
|
SOFT_LOCK(funnel->base);
|
|
spin_unlock(&g_trace_info->trace_lock);
|
|
}
|
|
exynos_etb_enable(g_etb_base, 0);
|
|
}
|
|
|
|
extern void exynos_etb_stm(void)
|
|
{
|
|
struct funnel_info *funnel;
|
|
unsigned int channel, port;
|
|
|
|
exynos_etb_disable(g_etb_base, 1);
|
|
|
|
if (stm_funnel_port[CHANNEL] != NONE) {
|
|
channel = stm_funnel_port[CHANNEL];
|
|
port = stm_funnel_port[PORT];
|
|
funnel = &g_trace_info->funnel[channel];
|
|
|
|
spin_lock(&g_trace_info->trace_lock);
|
|
SOFT_UNLOCK(funnel->base);
|
|
funnel->port_status = etm_readl(funnel->base, FUNCTRL);
|
|
funnel->port_status |= BIT(port);
|
|
etm_writel(funnel->base, funnel->port_status, FUNCTRL);
|
|
SOFT_LOCK(funnel->base);
|
|
spin_unlock(&g_trace_info->trace_lock);
|
|
}
|
|
exynos_etb_enable(g_etb_base, 1);
|
|
}
|
|
#endif
|
|
|
|
static int etm_info_init(void)
|
|
{
|
|
/* Main control and Configuration */
|
|
spin_lock_init(&g_trace_info->trace_lock);
|
|
|
|
g_trace_info->funnel_num = 0;
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_ETF
|
|
g_trace_info->etf_num = 0;
|
|
#endif
|
|
g_trace_info->procsel = 0;
|
|
g_trace_info->config = 0;
|
|
g_trace_info->sync_period = 0x8;
|
|
g_trace_info->victlr = 0x0;
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_ETR
|
|
g_trace_info->etr.buf_addr = exynos_ss_get_item_paddr("log_etm");
|
|
if (!g_trace_info->etr.buf_addr)
|
|
return -ENOMEM;
|
|
g_trace_info->etr.buf_size = exynos_ss_get_item_size("log_etm") / 4;
|
|
if (!g_trace_info->etr.buf_size)
|
|
return -ENOMEM;
|
|
g_trace_info->etr.buf_pointer = 0;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static void etm_enable(unsigned int cpu)
|
|
{
|
|
struct cpu_etm_info *cinfo = &g_trace_info->cpu[cpu];
|
|
struct funnel_info *funnel;
|
|
unsigned int channel, port;
|
|
|
|
SOFT_UNLOCK(cinfo->base);
|
|
etm_writel(cinfo->base, 0x1, ETMOSLAR);
|
|
etm_writel(cinfo->base, 0x0, ETMCTLR);
|
|
|
|
/* Main control and Configuration */
|
|
etm_writel(cinfo->base, g_trace_info->procsel, ETMPROCSELR);
|
|
etm_writel(cinfo->base, g_trace_info->config, ETMCONFIG);
|
|
etm_writel(cinfo->base, g_trace_info->sync_period, ETMSYNCPR);
|
|
|
|
etm_writel(cinfo->base, cpu+1, ETMTRACEIDR);
|
|
|
|
/* additional register setting */
|
|
etm_writel(cinfo->base, 0x1000, ETMEVENTCTL0R);
|
|
etm_writel(cinfo->base, 0x0, ETMEVENTCTL1R);
|
|
etm_writel(cinfo->base, 0xc, ETMSTALLCTLR);
|
|
etm_writel(cinfo->base, 0x801, ETMCONFIG);
|
|
etm_writel(cinfo->base, 0x0, ETMTSCTLR);
|
|
etm_writel(cinfo->base, 0x4, ETMCCCCTLR);
|
|
|
|
etm_writel(cinfo->base, 0x201, ETMVICTLR);
|
|
etm_writel(cinfo->base, 0x0, ETMVIIECTLR);
|
|
etm_writel(cinfo->base, 0x0, ETMVISSCTLR);
|
|
etm_writel(cinfo->base, 0x2, ETMAUXCTLR);
|
|
|
|
etm_writel(cinfo->base, 0x1, ETMCTLR);
|
|
etm_writel(cinfo->base, 0x0, ETMOSLAR);
|
|
SOFT_LOCK(cinfo->base);
|
|
|
|
channel = cinfo->f_port[CHANNEL];
|
|
port = cinfo->f_port[PORT];
|
|
funnel = &g_trace_info->funnel[channel];
|
|
|
|
spin_lock(&g_trace_info->trace_lock);
|
|
cinfo->enabled = 1;
|
|
SOFT_UNLOCK(funnel->base);
|
|
funnel->port_status = etm_readl(funnel->base, FUNCTRL);
|
|
funnel->port_status |= BIT(port);
|
|
etm_writel(funnel->base, funnel->port_status, FUNCTRL);
|
|
SOFT_LOCK(funnel->base);
|
|
spin_unlock(&g_trace_info->trace_lock);
|
|
}
|
|
|
|
static void etm_disable(unsigned int cpu)
|
|
{
|
|
struct cpu_etm_info *cinfo = &g_trace_info->cpu[cpu];
|
|
struct funnel_info *funnel;
|
|
unsigned int channel, port;
|
|
|
|
channel = cinfo->f_port[CHANNEL];
|
|
port = cinfo->f_port[PORT];
|
|
funnel = &g_trace_info->funnel[channel];
|
|
|
|
spin_lock(&g_trace_info->trace_lock);
|
|
cinfo->enabled = 0;
|
|
SOFT_UNLOCK(funnel->base);
|
|
funnel->port_status = etm_readl(funnel->base, FUNCTRL);
|
|
funnel->port_status &= ~BIT(port);
|
|
etm_writel(funnel->base, funnel->port_status, FUNCTRL);
|
|
SOFT_LOCK(funnel->base);
|
|
spin_unlock(&g_trace_info->trace_lock);
|
|
|
|
SOFT_UNLOCK(cinfo->base);
|
|
etm_writel(cinfo->base, 0x0, ETMCTLR);
|
|
etm_writel(cinfo->base, 0x1, ETMOSLAR);
|
|
SOFT_LOCK(cinfo->base);
|
|
}
|
|
|
|
extern void exynos_trace_start(void)
|
|
{
|
|
g_trace_info->enabled = 1;
|
|
|
|
exynos_funnel_init();
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_ETB
|
|
exynos_etb_enable(g_etb_base, 0);
|
|
#endif
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_ETF
|
|
exynos_etf_enable();
|
|
#endif
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_ETR
|
|
clk_prepare_enable(g_trace_info->etr.etr_clk);
|
|
exynos_etr_enable();
|
|
#endif
|
|
pr_info("coresight: %s.\n", __func__);
|
|
}
|
|
|
|
extern void exynos_trace_stop(void)
|
|
{
|
|
if (!g_trace_info->enabled)
|
|
return;
|
|
|
|
exynos_funnel_close();
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_ETF
|
|
exynos_etf_disable();
|
|
#endif
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_ETR
|
|
exynos_etr_disable();
|
|
clk_disable_unprepare(g_trace_info->etr.etr_clk);
|
|
#endif
|
|
etm_disable(raw_smp_processor_id());
|
|
|
|
g_trace_info->enabled = 0;
|
|
pr_info("coresight: %s.\n", __func__);
|
|
}
|
|
|
|
static void exynos_trace_ipi(void *info)
|
|
{
|
|
int *hcpu = (int *)info;
|
|
|
|
etm_disable(*hcpu);
|
|
}
|
|
|
|
static int __cpuinit core_notify(struct notifier_block *self,
|
|
unsigned long action, void *data)
|
|
{
|
|
int hcpu = (unsigned long)data;
|
|
|
|
switch (action) {
|
|
case CPU_DYING:
|
|
smp_call_function_single(hcpu, exynos_trace_ipi, &hcpu, 0);
|
|
break;
|
|
};
|
|
return NOTIFY_OK;
|
|
}
|
|
|
|
static struct notifier_block __cpuinitdata core_nb = {
|
|
.notifier_call = core_notify,
|
|
};
|
|
|
|
static int exynos_c2_etm_pm_notifier(struct notifier_block *self,
|
|
unsigned long action, void *v)
|
|
{
|
|
int cpu = raw_smp_processor_id();
|
|
|
|
switch (action) {
|
|
case CPU_PM_ENTER:
|
|
etm_disable(cpu);
|
|
break;
|
|
case CPU_PM_ENTER_FAILED:
|
|
case CPU_PM_EXIT:
|
|
etm_enable(cpu);
|
|
break;
|
|
case CPU_CLUSTER_PM_ENTER:
|
|
break;
|
|
case CPU_CLUSTER_PM_ENTER_FAILED:
|
|
case CPU_CLUSTER_PM_EXIT:
|
|
break;
|
|
}
|
|
return NOTIFY_OK;
|
|
}
|
|
|
|
static struct notifier_block __cpuinitdata exynos_c2_etm_pm_nb = {
|
|
.notifier_call = exynos_c2_etm_pm_notifier,
|
|
};
|
|
|
|
static int exynos_etm_pm_notifier(struct notifier_block *notifier,
|
|
unsigned long pm_event, void *v)
|
|
{
|
|
switch (pm_event) {
|
|
case PM_SUSPEND_PREPARE:
|
|
exynos_trace_stop();
|
|
break;
|
|
case PM_POST_SUSPEND:
|
|
exynos_trace_start();
|
|
break;
|
|
}
|
|
return NOTIFY_OK;
|
|
}
|
|
|
|
static struct notifier_block exynos_etm_pm_nb = {
|
|
.notifier_call = exynos_etm_pm_notifier,
|
|
};
|
|
|
|
#ifdef CONFIG_OF
|
|
static const struct of_device_id etm_dt_match[] = {
|
|
{ .compatible = "exynos,coresight",},
|
|
};
|
|
#endif
|
|
|
|
static int exynos_cs_etm_init_dt(void)
|
|
{
|
|
struct device_node *cs_np, *np = NULL;
|
|
unsigned int offset, cs_reg_base;
|
|
int i = 0;
|
|
|
|
g_trace_info = kzalloc(sizeof(struct exynos_trace_info), GFP_KERNEL);
|
|
if (!g_trace_info)
|
|
return -ENOMEM;
|
|
|
|
if (etm_info_init())
|
|
return -ENOMEM;
|
|
|
|
cs_np = of_find_matching_node(NULL, etm_dt_match);
|
|
|
|
if (of_property_read_u32(cs_np, "funnel-num", &g_trace_info->funnel_num))
|
|
return -EINVAL;
|
|
g_trace_info->funnel = kzalloc(sizeof(struct funnel_info) *
|
|
g_trace_info->funnel_num, GFP_KERNEL);
|
|
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_ETF
|
|
if (of_property_read_u32(cs_np, "etf-num", &g_trace_info->etf_num))
|
|
return -EINVAL;
|
|
g_trace_info->etf = kzalloc(sizeof(struct etf_info) *
|
|
g_trace_info->etf_num, GFP_KERNEL);
|
|
#endif
|
|
|
|
if (of_property_read_u32(cs_np, "base", &cs_reg_base))
|
|
return -EINVAL;
|
|
while ((np = of_find_node_by_type(np, "cs"))) {
|
|
if (of_property_read_u32(np, "etm-offset", &offset))
|
|
return -EINVAL;
|
|
|
|
g_trace_info->cpu[i].base = ioremap(cs_reg_base + offset, SZ_4K);
|
|
if (!g_trace_info->cpu[i].base)
|
|
return -ENOMEM;
|
|
if (of_property_read_u32_array(np, "funnel-port",
|
|
g_trace_info->cpu[i].f_port, 2))
|
|
g_trace_info->cpu[i].f_port[CHANNEL] = NONE;
|
|
i++;
|
|
}
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_ETF
|
|
i = 0;
|
|
while ((np = of_find_node_by_type(np, "etf"))) {
|
|
if (of_property_read_u32(np, "offset", &offset))
|
|
return -EINVAL;
|
|
|
|
g_trace_info->etf[i].base = ioremap(cs_reg_base + offset, SZ_4K);
|
|
if (!g_trace_info->etf[i].base)
|
|
return -ENOMEM;
|
|
if (of_property_read_u32_array(np, "funnel-port",
|
|
g_trace_info->etf[i].f_port, 2))
|
|
g_trace_info->etf[i].f_port[CHANNEL] = NONE;
|
|
i++;
|
|
}
|
|
#endif
|
|
i = 0;
|
|
while ((np = of_find_node_by_type(np, "funnel"))) {
|
|
if (of_property_read_u32(np, "offset", &offset))
|
|
return -EINVAL;
|
|
g_trace_info->funnel[i].base = ioremap(cs_reg_base + offset, SZ_4K);
|
|
|
|
if (!g_trace_info->funnel[i].base)
|
|
return -ENOMEM;
|
|
if (of_property_read_u32_array(np, "funnel-port",
|
|
g_trace_info->funnel[i].f_port, 2))
|
|
g_trace_info->funnel[i].f_port[CHANNEL] = NONE;
|
|
i++;
|
|
}
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_ETR
|
|
if (!(np = of_find_node_by_type(np, "etr")))
|
|
return -EINVAL;
|
|
if (of_property_read_u32(np, "offset", &offset))
|
|
return -EINVAL;
|
|
g_trace_info->etr.base = ioremap(cs_reg_base + offset, SZ_4K);
|
|
if (!g_trace_info->etr.base)
|
|
return -ENOMEM;
|
|
#endif
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_ETB
|
|
if (!(np = of_find_node_by_type(np, "etb")))
|
|
return -EINVAL;
|
|
if (of_property_read_u32(np, "offset", &offset))
|
|
return -EINVAL;
|
|
g_etb_base = ioremap(cs_reg_base + cs_offset, SZ_4K);
|
|
if (!g_etb_base)
|
|
return -ENOMEM;
|
|
#endif
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_STM
|
|
if (!(np = of_find_node_by_type(np, "stm")))
|
|
return -EINVAL;
|
|
if (of_property_read_u32_array(np, "funnel-port",
|
|
&stm_funnel_port, 2))
|
|
stm_funnel_port[CHANNEL] = NONE;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static void etm_hotplug_out(unsigned int cpu)
|
|
{
|
|
etm_disable(cpu);
|
|
}
|
|
|
|
static void etm_hotplug_in(unsigned int cpu)
|
|
{
|
|
etm_enable(cpu);
|
|
}
|
|
|
|
static int etm_should_run(unsigned int cpu) { return 0; }
|
|
|
|
static void etm_thread_fn(unsigned int cpu) { }
|
|
|
|
static struct smp_hotplug_thread etm_threads = {
|
|
.store = &etm_task,
|
|
.thread_should_run = etm_should_run,
|
|
.thread_fn = etm_thread_fn,
|
|
.thread_comm = "etm/%u",
|
|
.setup = etm_hotplug_in,
|
|
.park = etm_hotplug_out,
|
|
.unpark = etm_hotplug_in,
|
|
};
|
|
|
|
static int __init exynos_etm_init(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = exynos_cs_etm_init_dt();
|
|
if (ret < 0)
|
|
goto err;
|
|
|
|
ret = smpboot_register_percpu_thread(&etm_threads);
|
|
if (ret < 0)
|
|
goto err;
|
|
|
|
register_pm_notifier(&exynos_etm_pm_nb);
|
|
register_cpu_notifier(&core_nb);
|
|
cpu_pm_register_notifier(&exynos_c2_etm_pm_nb);
|
|
|
|
g_trace_info->enabled = 1;
|
|
pr_info("coresight: ETM enable.\n");
|
|
return 0;
|
|
err:
|
|
g_trace_info->enabled = 0;
|
|
pr_err("coresight: ETM enable FAILED!!! : ret = %d\n", ret);
|
|
return ret;
|
|
}
|
|
early_initcall(exynos_etm_init);
|
|
|
|
static int __init exynos_tmc_init(void)
|
|
{
|
|
if (!g_trace_info->enabled) {
|
|
pr_err("coresight TMC init FAILED!!!\n");
|
|
return -ENODEV;
|
|
}
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_ETR
|
|
g_trace_info->etr.etr_clk = clk_get(NULL, "etr_clk");
|
|
#endif
|
|
exynos_trace_start();
|
|
return 0;
|
|
}
|
|
postcore_initcall(exynos_tmc_init);
|
|
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_ETM_SYSFS
|
|
static struct bus_type etm_subsys = {
|
|
.name = "exynos-etm",
|
|
.dev_name = "exynos-etm",
|
|
};
|
|
|
|
static ssize_t etm_show_all_status(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
struct cpu_etm_info *cinfo;
|
|
struct funnel_info *funnel;
|
|
struct etf_info *etf;
|
|
unsigned long tmp, port_status, read_p;
|
|
int i, channel, port, size = 0;
|
|
|
|
size += scnprintf(buf + size, 15,"ETM Status\n");
|
|
size += scnprintf(buf + size, 80,
|
|
"-------------------------------------------------------------\n");
|
|
size += scnprintf(buf + size, 80, " %-8s | %-10s | %-14s | %-4s | %-11s\n",
|
|
"Core Num", "ETM status", "Funnel_channel", "Port", "Port Status");
|
|
for (i = 0; i < NR_CPUS; i++) {
|
|
cinfo = &g_trace_info->cpu[i];
|
|
channel = cinfo->f_port[CHANNEL];
|
|
port = cinfo->f_port[PORT];
|
|
funnel = &g_trace_info->funnel[channel];
|
|
spin_lock(&g_trace_info->trace_lock);
|
|
port_status = (funnel->port_status >> port) & 0x1;
|
|
spin_unlock(&g_trace_info->trace_lock);
|
|
size += scnprintf(buf + size, 80,
|
|
" %-8d | %10s | %-14u | %-4d | %-11s\n",
|
|
i, cinfo->enabled ? "enabled" : "disabled", channel, port,
|
|
port_status ? "open" : "close");
|
|
}
|
|
size += scnprintf(buf + size, 80,
|
|
"-------------------------------------------------------------\n");
|
|
for (i = 0; i < g_trace_info->funnel_num; i++) {
|
|
funnel = &g_trace_info->funnel[i];
|
|
SOFT_UNLOCK(funnel->base);
|
|
tmp = etm_readl(funnel->base, FUNCTRL);
|
|
SOFT_LOCK(funnel->base);
|
|
size += scnprintf(buf + size, 30, "FUNNEL%d Status : 0x%lx\n", i, tmp);
|
|
}
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_ETF
|
|
for (i = 0; i < g_trace_info->etf_num; i++) {
|
|
etf = &g_trace_info->etf[i];
|
|
SOFT_UNLOCK(etf->base);
|
|
tmp = etm_readl(etf->base, TMCCTL);
|
|
read_p = etm_readl(etf->base, TMCRWP);
|
|
SOFT_LOCK(etf->base);
|
|
size += scnprintf(buf + size, 30, "ETF%d Status : %3sabled\n",
|
|
i, tmp & 0x1 ? "en" : "dis");
|
|
size += scnprintf(buf + size, 30, "ETF%d RWP Reg : 0x%lx\n", i, read_p);
|
|
}
|
|
#endif
|
|
#ifdef CONFIG_EXYNOS_CORESIGHT_ETR
|
|
SOFT_UNLOCK(g_trace_info->etr.base);
|
|
tmp = etm_readl(g_trace_info->etr.base, TMCCTL);
|
|
read_p = etm_readl(g_trace_info->etr.base, TMCRWP);
|
|
SOFT_LOCK(g_trace_info->etr.base);
|
|
size += scnprintf(buf + size, 30, "ETR Status : %3sabled\n",
|
|
tmp & 0x1 ? "en" : "dis");
|
|
size += scnprintf(buf + size, 30, "ETR RWP Reg : 0x%lx\n", read_p);
|
|
size += scnprintf(buf + size, 30, "ETR save RWP : 0x%x\n\n",
|
|
g_trace_info->etr.buf_pointer);
|
|
#endif
|
|
return size;
|
|
}
|
|
|
|
static struct kobj_attribute etm_enable_attr =
|
|
__ATTR(etm_status, 0644, etm_show_all_status, NULL);
|
|
|
|
static struct attribute *etm_sysfs_attrs[] = {
|
|
&etm_enable_attr.attr,
|
|
NULL,
|
|
};
|
|
|
|
static struct attribute_group etm_sysfs_group = {
|
|
.attrs = etm_sysfs_attrs,
|
|
};
|
|
|
|
static const struct attribute_group *etm_sysfs_groups[] = {
|
|
&etm_sysfs_group,
|
|
NULL,
|
|
};
|
|
|
|
static int __init exynos_etm_sysfs_init(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = subsys_system_register(&etm_subsys, etm_sysfs_groups);
|
|
if (ret)
|
|
pr_err("fail to register exynos-etm subsys\n");
|
|
|
|
return ret;
|
|
}
|
|
late_initcall(exynos_etm_sysfs_init);
|
|
#endif
|