| /* |
| * 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/suspend.h> |
| #include <linux/smpboot.h> |
| #include <linux/delay.h> |
| #include <linux/exynos-ss.h> |
| |
| #include <asm/core_regs.h> |
| #include <asm/io.h> |
| #include <asm/smp_plat.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; |
| 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 inline u32 logical_to_phy_cpu(unsigned int cpu) |
| { |
| u32 mpidr = cpu_logical_map(cpu); |
| return (MPIDR_AFFINITY_LEVEL(mpidr, 1) << 2 |
| | MPIDR_AFFINITY_LEVEL(mpidr, 0)); |
| } |
| |
| 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) |
| { |
| int core = logical_to_phy_cpu(cpu); |
| struct cpu_etm_info *cinfo = &g_trace_info->cpu[core]; |
| 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) |
| { |
| int core = logical_to_phy_cpu(cpu); |
| struct cpu_etm_info *cinfo = &g_trace_info->cpu[core]; |
| 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 |
| 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(); |
| #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 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 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 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; |
| } |
| 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 |