Merge commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126' into msm-3.4
AU_LINUX_ANDROID_ICS.04.00.04.00.126 from msm-3.0.
First parent is from google/android-3.4.
* commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126': (8712 commits)
PRNG: Device tree entry for qrng device.
vidc:1080p: Set video core timeout value for Thumbnail mode
msm: sps: improve the debugging support in SPS driver
board-8064 msm: Overlap secure and non secure video firmware heaps.
msm: clock: Add handoff ops for 7x30 and copper XO clocks
msm_fb: display: Wait for external vsync before DTV IOMMU unmap
msm: Fix ciruclar dependency in debug UART settings
msm: gdsc: Add GDSC regulator driver for msm-copper
defconfig: Enable Mobicore Driver.
mobicore: Add mobicore driver.
mobicore: rename variable to lower case.
mobicore: rename folder.
mobicore: add makefiles
mobicore: initial import of kernel driver
ASoC: msm: Add SLIMBUS_2_RX CPU DAI
board-8064-gpio: Update FUNC for EPM SPI CS
msm_fb: display: Remove chicken bit config during video playback
mmc: msm_sdcc: enable the sanitize capability
msm-fb: display: lm2 writeback support on mpq platfroms
msm_fb: display: Disable LVDS phy & pll during panel off
...
Signed-off-by: Steve Muckle <smuckle@codeaurora.org>
diff --git a/arch/arm/mach-msm/etm.c b/arch/arm/mach-msm/etm.c
new file mode 100644
index 0000000..6cceff2
--- /dev/null
+++ b/arch/arm/mach-msm/etm.c
@@ -0,0 +1,1037 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/miscdevice.h>
+#include <linux/cpu.h>
+#include <linux/smp.h>
+#include <linux/percpu.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/wakelock.h>
+#include <linux/pm_qos.h>
+
+#include <asm/atomic.h>
+
+#include "cp14.h"
+
+#define LOG_BUF_LEN 32768
+/* each slot is 4 bytes, 8kb total */
+#define ETB_RAM_SLOTS 2048
+
+#define DATALOG_SYNC 0xB5C7
+#define ETM_DUMP_MSG_ID 0x000A6960
+#define ETB_DUMP_MSG_ID 0x000A6961
+
+/* ETB Registers */
+#define ETB_REG_CONTROL ETMIMPSPEC1
+#define ETB_REG_STATUS ETMIMPSPEC2
+#define ETB_REG_COUNT ETMIMPSPEC3
+#define ETB_REG_ADDRESS ETMIMPSPEC4
+#define ETB_REG_DATA ETMIMPSPEC5
+
+/* Having etb macro accessors allows macro expansion for ETB reg defines */
+#define etb_read(reg) etm_read(reg)
+#define etb_write(val, reg) etm_write(val, reg)
+
+/* Bitmasks for the ETM control register */
+#define ETM_CONTROL_POWERDOWN 0x00000001
+#define ETM_CONTROL_PROGRAM 0x00000400
+
+/* Bitmasks for the ETM status register */
+#define ETM_STATUS_PROGRAMMING 0x00000002
+
+/* ETB Status Register bit definitions */
+#define OV 0x00200000
+
+/* ETB Control Register bit definitions */
+#define AIR 0x00000008
+#define AIW 0x00000004
+#define CPTM 0x00000002
+#define CPTEN 0x00000001
+
+/* Bitmasks for the swconfig field of ETM_CONFIG
+ * ETM trigger propagated to ETM instances on all cores
+ */
+#define TRIGGER_ALL 0x00000002
+
+#define PROG_TIMEOUT_MS 500
+
+static int trace_enabled;
+static int cpu_to_dump;
+static int next_cpu_to_dump;
+static struct wake_lock etm_wake_lock;
+static struct pm_qos_request etm_qos_req;
+static int trace_on_boot;
+module_param_named(
+ trace_on_boot, trace_on_boot, int, S_IRUGO
+);
+
+struct b {
+ uint8_t etm_log_buf[LOG_BUF_LEN];
+ uint32_t log_end;
+};
+
+static struct b buf[NR_CPUS];
+static struct b __percpu * *alloc_b;
+static atomic_t etm_dev_in_use;
+
+/* These default settings will be used to configure the ETM/ETB
+ * when the driver loads. */
+struct etm_config_struct {
+ uint32_t etm_00_control;
+ uint32_t etm_02_trigger_event;
+ uint32_t etm_06_te_start_stop;
+ uint32_t etm_07_te_single_addr_comp;
+ uint32_t etm_08_te_event;
+ uint32_t etm_09_te_control;
+ uint32_t etm_0a_fifofull_region;
+ uint32_t etm_0b_fifofull_level;
+ uint32_t etm_0c_vd_event;
+ uint32_t etm_0d_vd_single_addr_comp;
+ uint32_t etm_0e_vd_mmd;
+ uint32_t etm_0f_vd_control;
+ uint32_t etm_addr_comp_value[8]; /* 10 to 17 */
+ uint32_t etm_addr_access_type[8]; /* 20 to 27 */
+ uint32_t etm_data_comp_value[2]; /* 30 and 32 */
+ uint32_t etm_data_comp_mask[2]; /* 40 and 42 */
+ uint32_t etm_counter_reload_value[2]; /* 50 to 51 */
+ uint32_t etm_counter_enable[2]; /* 54 to 55 */
+ uint32_t etm_counter_reload_event[2]; /* 58 to 59 */
+ uint32_t etm_60_seq_event_1_to_2;
+ uint32_t etm_61_seq_event_2_to_1;
+ uint32_t etm_62_seq_event_2_to_3;
+ uint32_t etm_63_seq_event_3_to_1;
+ uint32_t etm_64_seq_event_3_to_2;
+ uint32_t etm_65_seq_event_1_to_3;
+ uint32_t etm_6c_cid_comp_value_1;
+ uint32_t etm_6f_cid_comp_mask;
+ uint32_t etm_78_sync_freq;
+ uint32_t swconfig;
+ uint32_t etb_trig_cnt;
+ uint32_t etb_init_ptr;
+};
+
+static struct etm_config_struct etm_config = {
+ /* etm_00_control 0x0000D84E: 32-bit CID, cycle-accurate,
+ * monitorCPRT */
+ .etm_00_control = 0x0000D84E,
+ /* etm_02_trigger_event 0x00000000: address comparator 0 matches */
+ .etm_02_trigger_event = 0x00000000,
+ .etm_06_te_start_stop = 0x00000000,
+ .etm_07_te_single_addr_comp = 0x00000000,
+ /* etm_08_te_event 0x0000006F: always true */
+ .etm_08_te_event = 0x0000006F,
+ /* etm_09_te_control 0x01000000: exclude none */
+ .etm_09_te_control = 0x01000000,
+ .etm_0a_fifofull_region = 0x00000000,
+ .etm_0b_fifofull_level = 0x00000000,
+ /* etm_0c_vd_event 0x0000006F: always true */
+ .etm_0c_vd_event = 0x0000006F,
+ .etm_0d_vd_single_addr_comp = 0x00000000,
+ .etm_0e_vd_mmd = 0x00000000,
+ /* etm_0f_vd_control 0x00010000: exclude none */
+ .etm_0f_vd_control = 0x00010000,
+ .etm_addr_comp_value[0] = 0x00000000,
+ .etm_addr_comp_value[1] = 0x00000000,
+ .etm_addr_comp_value[2] = 0x00000000,
+ .etm_addr_comp_value[3] = 0x00000000,
+ .etm_addr_comp_value[4] = 0x00000000,
+ .etm_addr_comp_value[5] = 0x00000000,
+ .etm_addr_comp_value[6] = 0x00000000,
+ .etm_addr_comp_value[7] = 0x00000000,
+ .etm_addr_access_type[0] = 0x00000000,
+ .etm_addr_access_type[1] = 0x00000000,
+ .etm_addr_access_type[2] = 0x00000000,
+ .etm_addr_access_type[3] = 0x00000000,
+ .etm_addr_access_type[4] = 0x00000000,
+ .etm_addr_access_type[5] = 0x00000000,
+ .etm_addr_access_type[6] = 0x00000000,
+ .etm_addr_access_type[7] = 0x00000000,
+ .etm_data_comp_value[0] = 0x00000000,
+ .etm_data_comp_value[1] = 0x00000000,
+ .etm_data_comp_mask[0] = 0x00000000,
+ .etm_data_comp_mask[1] = 0x00000000,
+ .etm_counter_reload_value[0] = 0x00000000,
+ .etm_counter_reload_value[1] = 0x00000000,
+ .etm_counter_enable[0] = 0x0002406F,
+ .etm_counter_enable[1] = 0x0002406F,
+ .etm_counter_reload_event[0] = 0x0000406F,
+ .etm_counter_reload_event[1] = 0x0000406F,
+ .etm_60_seq_event_1_to_2 = 0x0000406F,
+ .etm_61_seq_event_2_to_1 = 0x0000406F,
+ .etm_62_seq_event_2_to_3 = 0x0000406F,
+ .etm_63_seq_event_3_to_1 = 0x0000406F,
+ .etm_64_seq_event_3_to_2 = 0x0000406F,
+ .etm_65_seq_event_1_to_3 = 0x0000406F,
+ .etm_6c_cid_comp_value_1 = 0x00000000,
+ .etm_6f_cid_comp_mask = 0x00000000,
+ .etm_78_sync_freq = 0x00000400,
+ .swconfig = 0x00000002,
+ /* etb_trig_cnt 0x00000000: ignore trigger */
+ .etb_trig_cnt = 0x00000000,
+ /* etb_init_ptr 0x00000010: 16 marker bytes */
+ .etb_init_ptr = 0x00000010,
+};
+
+/* ETM clock is derived from the processor clock and gets enabled on a
+ * logical OR of below items on Scorpion:
+ * 1.CPMR[ETMCLKEN] is set
+ * 2.ETM is not idle. Also means ETMCR[PD] is 0
+ * 3.Reset is asserted (core or debug)
+ * 4.MRC/MCR to ETM reg (CP14 access)
+ * 5.Debugger access to a ETM register in the core power domain
+ *
+ * 1. and 2. above are permanent enables whereas 3., 4. and 5. are
+ * temporary enables
+ *
+ * We rely on 4. to be able to access ETMCR and then use 2. above for ETM
+ * clock vote in the driver and the save-restore code uses 1. above
+ * for its vote.
+ */
+static inline void __cpu_set_etm_pwrdwn(void)
+{
+ uint32_t etm_control;
+
+ isb();
+ etm_control = etm_read(ETMCR);
+ etm_control |= ETM_CONTROL_POWERDOWN;
+ etm_write(etm_control, ETMCR);
+}
+
+static inline void __cpu_clear_etm_pwrdwn(void)
+{
+ uint32_t etm_control;
+
+ etm_control = etm_read(ETMCR);
+ etm_control &= ~ETM_CONTROL_POWERDOWN;
+ etm_write(etm_control, ETMCR);
+ isb();
+}
+
+static void emit_log_char(uint8_t c)
+{
+ int this_cpu = get_cpu();
+ struct b *mybuf = *per_cpu_ptr(alloc_b, this_cpu);
+ char *log_buf = mybuf->etm_log_buf;
+ int index = (mybuf->log_end)++ & (LOG_BUF_LEN - 1);
+ log_buf[index] = c;
+ put_cpu();
+}
+
+static void emit_log_word(uint32_t word)
+{
+ emit_log_char(word >> 24);
+ emit_log_char(word >> 16);
+ emit_log_char(word >> 8);
+ emit_log_char(word >> 0);
+}
+
+static void __cpu_enable_etb(void)
+{
+ uint32_t etb_control;
+ uint32_t i;
+
+ /* enable auto-increment on reads and writes */
+ etb_control = AIR | AIW;
+ etb_write(etb_control, ETB_REG_CONTROL);
+
+ /* write tags to the slots before the write pointer so we can
+ * detect overflow */
+ etb_write(0x00000000, ETB_REG_ADDRESS);
+ for (i = 0; i < (etm_config.etb_init_ptr >> 2); i++)
+ etb_write(0xDEADBEEF, ETB_REG_DATA);
+
+ etb_write(0x00000000, ETB_REG_STATUS);
+
+ /* initialize write pointer */
+ etb_write(etm_config.etb_init_ptr, ETB_REG_ADDRESS);
+
+ /* multiple of 16 */
+ etb_write(etm_config.etb_trig_cnt & 0xFFFFFFF0, ETB_REG_COUNT);
+
+ /* Enable ETB and enable the trigger counter as appropriate. A
+ * trigger count of 0 will be used to signify that the user wants to
+ * ignore the trigger (just keep writing to the ETB and overwriting
+ * the oldest data). For "trace before trigger" captures the user
+ * should set the trigger count to a small number. */
+
+ etb_control |= CPTEN;
+ if (etm_config.etb_trig_cnt)
+ etb_control |= CPTM;
+ etb_write(etb_control, ETB_REG_CONTROL);
+}
+
+static void __cpu_disable_etb(void)
+{
+ uint32_t etb_control;
+ etb_control = etb_read(ETB_REG_CONTROL);
+ etb_control &= ~CPTEN;
+ etb_write(etb_control, ETB_REG_CONTROL);
+}
+
+static void __cpu_enable_etm(void)
+{
+ uint32_t etm_control;
+ unsigned long timeout = jiffies + msecs_to_jiffies(PROG_TIMEOUT_MS);
+
+ etm_control = etm_read(ETMCR);
+ etm_control &= ~ETM_CONTROL_PROGRAM;
+ etm_write(etm_control, ETMCR);
+
+ while ((etm_read(ETMSR) & ETM_STATUS_PROGRAMMING) == 1) {
+ cpu_relax();
+ if (time_after(jiffies, timeout)) {
+ pr_err("etm: timeout while clearing prog bit\n");
+ break;
+ }
+ }
+}
+
+static void __cpu_disable_etm(void)
+{
+ uint32_t etm_control;
+ unsigned long timeout = jiffies + msecs_to_jiffies(PROG_TIMEOUT_MS);
+
+ etm_control = etm_read(ETMCR);
+ etm_control |= ETM_CONTROL_PROGRAM;
+ etm_write(etm_control, ETMCR);
+
+ while ((etm_read(ETMSR) & ETM_STATUS_PROGRAMMING) == 0) {
+ cpu_relax();
+ if (time_after(jiffies, timeout)) {
+ pr_err("etm: timeout while setting prog bit\n");
+ break;
+ }
+ }
+}
+
+static void __cpu_enable_trace(void *unused)
+{
+ uint32_t etm_control;
+ uint32_t etm_trigger;
+ uint32_t etm_external_output;
+
+ get_cpu();
+
+ __cpu_disable_etb();
+ /* vote for ETM power/clock enable */
+ __cpu_clear_etm_pwrdwn();
+ __cpu_disable_etm();
+
+ etm_control = (etm_config.etm_00_control & ~ETM_CONTROL_POWERDOWN)
+ | ETM_CONTROL_PROGRAM;
+ etm_write(etm_control, ETMCR);
+
+ etm_trigger = etm_config.etm_02_trigger_event;
+ etm_external_output = 0x406F; /* always FALSE */
+
+ if (etm_config.swconfig & TRIGGER_ALL) {
+ uint32_t function = 0x5; /* A OR B */
+ uint32_t resource_b = 0x60; /* external input 1 */
+
+ etm_trigger &= 0x7F; /* keep resource A, clear function and
+ * resource B */
+ etm_trigger |= (function << 14);
+ etm_trigger |= (resource_b << 7);
+ etm_external_output = etm_trigger;
+ }
+
+ etm_write(etm_trigger, ETMTRIGGER);
+ etm_write(etm_config.etm_06_te_start_stop, ETMTSSCR);
+ etm_write(etm_config.etm_07_te_single_addr_comp, ETMTECR2);
+ etm_write(etm_config.etm_08_te_event, ETMTEEVR);
+ etm_write(etm_config.etm_09_te_control, ETMTECR1);
+ etm_write(etm_config.etm_0a_fifofull_region, ETMFFRR);
+ etm_write(etm_config.etm_0b_fifofull_level, ETMFFLR);
+ etm_write(etm_config.etm_0c_vd_event, ETMVDEVR);
+ etm_write(etm_config.etm_0d_vd_single_addr_comp, ETMVDCR1);
+ etm_write(etm_config.etm_0e_vd_mmd, ETMVDCR2);
+ etm_write(etm_config.etm_0f_vd_control, ETMVDCR3);
+ etm_write(etm_config.etm_addr_comp_value[0], ETMACVR0);
+ etm_write(etm_config.etm_addr_comp_value[1], ETMACVR1);
+ etm_write(etm_config.etm_addr_comp_value[2], ETMACVR2);
+ etm_write(etm_config.etm_addr_comp_value[3], ETMACVR3);
+ etm_write(etm_config.etm_addr_comp_value[4], ETMACVR4);
+ etm_write(etm_config.etm_addr_comp_value[5], ETMACVR5);
+ etm_write(etm_config.etm_addr_comp_value[6], ETMACVR6);
+ etm_write(etm_config.etm_addr_comp_value[7], ETMACVR7);
+ etm_write(etm_config.etm_addr_access_type[0], ETMACTR0);
+ etm_write(etm_config.etm_addr_access_type[1], ETMACTR1);
+ etm_write(etm_config.etm_addr_access_type[2], ETMACTR2);
+ etm_write(etm_config.etm_addr_access_type[3], ETMACTR3);
+ etm_write(etm_config.etm_addr_access_type[4], ETMACTR4);
+ etm_write(etm_config.etm_addr_access_type[5], ETMACTR5);
+ etm_write(etm_config.etm_addr_access_type[6], ETMACTR6);
+ etm_write(etm_config.etm_addr_access_type[7], ETMACTR7);
+ etm_write(etm_config.etm_data_comp_value[0], ETMDCVR0);
+ etm_write(etm_config.etm_data_comp_value[1], ETMDCVR2);
+ etm_write(etm_config.etm_data_comp_mask[0], ETMDCMR0);
+ etm_write(etm_config.etm_data_comp_mask[1], ETMDCMR2);
+ etm_write(etm_config.etm_counter_reload_value[0], ETMCNTRLDVR0);
+ etm_write(etm_config.etm_counter_reload_value[1], ETMCNTRLDVR1);
+ etm_write(etm_config.etm_counter_enable[0], ETMCNTENR0);
+ etm_write(etm_config.etm_counter_enable[1], ETMCNTENR1);
+ etm_write(etm_config.etm_counter_reload_event[0], ETMCNTRLDEVR0);
+ etm_write(etm_config.etm_counter_reload_event[1], ETMCNTRLDEVR1);
+ etm_write(etm_config.etm_60_seq_event_1_to_2, ETMSQ12EVR);
+ etm_write(etm_config.etm_61_seq_event_2_to_1, ETMSQ21EVR);
+ etm_write(etm_config.etm_62_seq_event_2_to_3, ETMSQ23EVR);
+ etm_write(etm_config.etm_63_seq_event_3_to_1, ETMSQ31EVR);
+ etm_write(etm_config.etm_64_seq_event_3_to_2, ETMSQ32EVR);
+ etm_write(etm_config.etm_65_seq_event_1_to_3, ETMSQ13EVR);
+ etm_write(etm_external_output, ETMEXTOUTEVR0);
+ etm_write(etm_config.etm_6c_cid_comp_value_1, ETMCIDCVR0);
+ etm_write(etm_config.etm_6f_cid_comp_mask, ETMCIDCMR);
+ etm_write(etm_config.etm_78_sync_freq, ETMSYNCFR);
+
+ /* Note that we must enable the ETB before we enable the ETM if we
+ * want to capture the "always true" trigger event. */
+
+ __cpu_enable_etb();
+ __cpu_enable_etm();
+
+ put_cpu();
+}
+
+static void __cpu_disable_trace(void *unused)
+{
+ get_cpu();
+
+ __cpu_disable_etm();
+
+ /* program trace enable to be low by using always false event */
+ etm_write(0x6F | BIT(14), ETMTEEVR);
+ /* vote for ETM power/clock disable */
+ __cpu_set_etm_pwrdwn();
+
+ __cpu_disable_etb();
+
+ put_cpu();
+}
+
+static void enable_trace(void)
+{
+ wake_lock(&etm_wake_lock);
+ pm_qos_update_request(&etm_qos_req, 0);
+
+ if (etm_config.swconfig & TRIGGER_ALL) {
+ /* This register is accessible from either core.
+ * CPU1_extout[0] -> CPU0_extin[0]
+ * CPU_extout[0] -> CPU1_extin[0] */
+ asm volatile("mcr p15, 3, %0, c15, c5, 2" : : "r" (0x1));
+ asm volatile("isb");
+ }
+
+ get_cpu();
+ __cpu_enable_trace(NULL);
+ smp_call_function(__cpu_enable_trace, NULL, 1);
+ put_cpu();
+
+ /* 1. causes all online cpus to come out of idle PC
+ * 2. prevents idle PC until save restore flag is enabled atomically
+ *
+ * we rely on the user to prevent hotplug on/off racing with this
+ * operation and to ensure cores where trace is expected to be turned
+ * on are already hotplugged on
+ */
+ trace_enabled = 1;
+
+ pm_qos_update_request(&etm_qos_req, PM_QOS_DEFAULT_VALUE);
+ wake_unlock(&etm_wake_lock);
+}
+
+static void disable_trace(void)
+{
+ wake_lock(&etm_wake_lock);
+ pm_qos_update_request(&etm_qos_req, 0);
+
+ get_cpu();
+ __cpu_disable_trace(NULL);
+ smp_call_function(__cpu_disable_trace, NULL, 1);
+ put_cpu();
+
+ /* 1. causes all online cpus to come out of idle PC
+ * 2. prevents idle PC until save restore flag is disabled atomically
+ *
+ * we rely on the user to prevent hotplug on/off racing with this
+ * operation and to ensure cores where trace is expected to be turned
+ * off are already hotplugged on
+ */
+ trace_enabled = 0;
+
+ cpu_to_dump = next_cpu_to_dump = 0;
+
+ pm_qos_update_request(&etm_qos_req, PM_QOS_DEFAULT_VALUE);
+ wake_unlock(&etm_wake_lock);
+}
+
+static void generate_etb_dump(void)
+{
+ uint32_t i;
+ uint32_t full_slots;
+ uint32_t etb_control;
+ uint32_t prim_len;
+ uint32_t uptime = 0;
+
+ etb_control = etb_read(ETB_REG_CONTROL);
+ etb_control |= AIR;
+ etb_write(etb_control, ETB_REG_CONTROL);
+
+ if (etb_read(ETB_REG_STATUS) & OV)
+ full_slots = ETB_RAM_SLOTS;
+ else
+ full_slots = etb_read(ETB_REG_ADDRESS) >> 2;
+
+ prim_len = 28 + (full_slots * 4);
+
+ emit_log_char((DATALOG_SYNC >> 8) & 0xFF);
+ emit_log_char((DATALOG_SYNC >> 0) & 0xFF);
+ emit_log_char((prim_len >> 8) & 0xFF);
+ emit_log_char((prim_len >> 0) & 0xFF);
+ emit_log_word(uptime);
+ emit_log_word(ETB_DUMP_MSG_ID);
+ emit_log_word(etm_read(ETMCR));
+ emit_log_word(etm_config.etb_init_ptr >> 2);
+ emit_log_word(etb_read(ETB_REG_ADDRESS) >> 2);
+ emit_log_word((etb_read(ETB_REG_STATUS) & OV) >> 21);
+
+ etb_write(0x00000000, ETB_REG_ADDRESS);
+ for (i = 0; i < full_slots; i++)
+ emit_log_word(etb_read(ETB_REG_DATA));
+}
+
+/* This should match the number of ETM registers being dumped below */
+#define ETM_NUM_REGS_TO_DUMP 54
+static void generate_etm_dump(void)
+{
+ uint32_t prim_len;
+ uint32_t uptime = 0;
+
+ prim_len = 12 + (4 * ETM_NUM_REGS_TO_DUMP);
+
+ emit_log_char((DATALOG_SYNC >> 8) & 0xFF);
+ emit_log_char((DATALOG_SYNC >> 0) & 0xFF);
+ emit_log_char((prim_len >> 8) & 0xFF);
+ emit_log_char((prim_len >> 0) & 0xFF);
+ emit_log_word(uptime);
+ emit_log_word(ETM_DUMP_MSG_ID);
+
+ emit_log_word(etm_read(ETMCR));
+ emit_log_word(etm_read(ETMSR));
+ emit_log_word(etb_read(ETB_REG_CONTROL));
+ emit_log_word(etb_read(ETB_REG_STATUS));
+ emit_log_word(etb_read(ETB_REG_COUNT));
+ emit_log_word(etb_read(ETB_REG_ADDRESS));
+ emit_log_word(0); /* don't read ETB_REG_DATA, changes ETB_REG_ADDRESS */
+ emit_log_word(etm_read(ETMTRIGGER));
+ emit_log_word(etm_read(ETMTSSCR));
+ emit_log_word(etm_read(ETMTECR2));
+ emit_log_word(etm_read(ETMTEEVR));
+ emit_log_word(etm_read(ETMTECR1));
+ emit_log_word(etm_read(ETMFFRR));
+ emit_log_word(etm_read(ETMFFLR));
+ emit_log_word(etm_read(ETMVDEVR));
+ emit_log_word(etm_read(ETMVDCR1));
+ emit_log_word(etm_read(ETMVDCR2));
+ emit_log_word(etm_read(ETMVDCR3));
+ emit_log_word(etm_read(ETMACVR0));
+ emit_log_word(etm_read(ETMACVR1));
+ emit_log_word(etm_read(ETMACVR2));
+ emit_log_word(etm_read(ETMACVR3));
+ emit_log_word(etm_read(ETMACVR4));
+ emit_log_word(etm_read(ETMACVR5));
+ emit_log_word(etm_read(ETMACVR6));
+ emit_log_word(etm_read(ETMACVR7));
+ emit_log_word(etm_read(ETMACTR0));
+ emit_log_word(etm_read(ETMACTR1));
+ emit_log_word(etm_read(ETMACTR2));
+ emit_log_word(etm_read(ETMACTR3));
+ emit_log_word(etm_read(ETMACTR4));
+ emit_log_word(etm_read(ETMACTR5));
+ emit_log_word(etm_read(ETMACTR6));
+ emit_log_word(etm_read(ETMACTR7));
+ emit_log_word(etm_read(ETMDCVR0));
+ emit_log_word(etm_read(ETMDCVR2));
+ emit_log_word(etm_read(ETMDCMR0));
+ emit_log_word(etm_read(ETMDCMR2));
+ emit_log_word(etm_read(ETMCNTRLDVR0));
+ emit_log_word(etm_read(ETMCNTRLDVR1));
+ emit_log_word(etm_read(ETMCNTENR0));
+ emit_log_word(etm_read(ETMCNTENR1));
+ emit_log_word(etm_read(ETMCNTRLDEVR0));
+ emit_log_word(etm_read(ETMCNTRLDEVR1));
+ emit_log_word(etm_read(ETMSQ12EVR));
+ emit_log_word(etm_read(ETMSQ21EVR));
+ emit_log_word(etm_read(ETMSQ23EVR));
+ emit_log_word(etm_read(ETMSQ31EVR));
+ emit_log_word(etm_read(ETMSQ32EVR));
+ emit_log_word(etm_read(ETMSQ13EVR));
+ emit_log_word(etm_read(ETMEXTOUTEVR0));
+ emit_log_word(etm_read(ETMCIDCVR0));
+ emit_log_word(etm_read(ETMCIDCMR));
+ emit_log_word(etm_read(ETMSYNCFR));
+}
+
+static void dump_all(void *unused)
+{
+ get_cpu();
+ __cpu_disable_etb();
+ generate_etm_dump();
+ generate_etb_dump();
+ if (trace_enabled)
+ __cpu_enable_etb();
+ put_cpu();
+}
+
+static void dump_trace(void)
+{
+ get_cpu();
+ dump_all(NULL);
+ smp_call_function(dump_all, NULL, 1);
+ put_cpu();
+}
+
+static int bytes_to_dump;
+static uint8_t *etm_buf_ptr;
+
+static int etm_dev_open(struct inode *inode, struct file *file)
+{
+ if (atomic_cmpxchg(&etm_dev_in_use, 0, 1))
+ return -EBUSY;
+
+ pr_debug("%s: successfully opened\n", __func__);
+ return 0;
+}
+
+static ssize_t etm_dev_read(struct file *file, char __user *data,
+ size_t len, loff_t *ppos)
+{
+ if (cpu_to_dump == next_cpu_to_dump) {
+ if (cpu_to_dump == 0)
+ dump_trace();
+ bytes_to_dump = buf[cpu_to_dump].log_end;
+ buf[cpu_to_dump].log_end = 0;
+ etm_buf_ptr = buf[cpu_to_dump].etm_log_buf;
+ next_cpu_to_dump++;
+ if (next_cpu_to_dump >= num_possible_cpus())
+ next_cpu_to_dump = 0;
+ }
+
+ if (len > bytes_to_dump)
+ len = bytes_to_dump;
+
+ if (copy_to_user(data, etm_buf_ptr, len)) {
+ pr_debug("%s: copy_to_user failed\n", __func__);
+ return -EFAULT;
+ }
+
+ bytes_to_dump -= len;
+ etm_buf_ptr += len;
+
+ pr_debug("%s: %d bytes copied, %d bytes left (cpu %d)\n",
+ __func__, len, bytes_to_dump, next_cpu_to_dump);
+ return len;
+}
+
+static void setup_range_filter(char addr_type, char range, uint32_t reg1,
+ uint32_t addr1, uint32_t reg2, uint32_t addr2)
+{
+ etm_config.etm_addr_comp_value[reg1] = addr1;
+ etm_config.etm_addr_comp_value[reg2] = addr2;
+
+ etm_config.etm_07_te_single_addr_comp |= (1 << reg1);
+ etm_config.etm_07_te_single_addr_comp |= (1 << reg2);
+
+ etm_config.etm_09_te_control |= (1 << (reg1/2));
+ if (range == 'i')
+ etm_config.etm_09_te_control &= ~(1 << 24);
+ else if (range == 'e')
+ etm_config.etm_09_te_control |= (1 << 24);
+
+ if (addr_type == 'i') {
+ etm_config.etm_addr_access_type[reg1] = 0x99;
+ etm_config.etm_addr_access_type[reg2] = 0x99;
+ } else if (addr_type == 'd') {
+ etm_config.etm_addr_access_type[reg1] = 0x9C;
+ etm_config.etm_addr_access_type[reg2] = 0x9C;
+ }
+}
+
+static void setup_start_stop_filter(char addr_type, char start_stop,
+ uint32_t reg, uint32_t addr)
+{
+ etm_config.etm_addr_comp_value[reg] = addr;
+
+ if (start_stop == 's')
+ etm_config.etm_06_te_start_stop |= (1 << reg);
+ else if (start_stop == 't')
+ etm_config.etm_06_te_start_stop |= (1 << (reg + 16));
+
+ etm_config.etm_09_te_control |= (1 << 25);
+
+ if (addr_type == 'i')
+ etm_config.etm_addr_access_type[reg] = 0x99;
+ else if (addr_type == 'd')
+ etm_config.etm_addr_access_type[reg] = 0x9C;
+}
+
+static void setup_viewdata_range_filter(char range, uint32_t reg1,
+ uint32_t addr1, uint32_t reg2, uint32_t addr2)
+{
+ etm_config.etm_addr_comp_value[reg1] = addr1;
+ etm_config.etm_addr_comp_value[reg2] = addr2;
+
+ if (range == 'i') {
+ etm_config.etm_0d_vd_single_addr_comp |= (1 << reg1);
+ etm_config.etm_0d_vd_single_addr_comp |= (1 << reg2);
+ etm_config.etm_0f_vd_control |= (1 << (reg1/2));
+ } else if (range == 'e') {
+ etm_config.etm_0d_vd_single_addr_comp |= (1 << (reg1 + 16));
+ etm_config.etm_0d_vd_single_addr_comp |= (1 << (reg2 + 16));
+ etm_config.etm_0f_vd_control |= (1 << ((reg1/2) + 8));
+ }
+ etm_config.etm_0f_vd_control &= ~(1 << 16);
+
+ etm_config.etm_addr_access_type[reg1] = 0x9C;
+ etm_config.etm_addr_access_type[reg2] = 0x9C;
+}
+
+static void setup_viewdata_start_stop_filter(char start_stop, uint32_t reg,
+ uint32_t addr)
+{
+ etm_config.etm_addr_comp_value[reg] = addr;
+
+ if (start_stop == 's')
+ etm_config.etm_06_te_start_stop |= (1 << reg);
+ else if (start_stop == 't')
+ etm_config.etm_06_te_start_stop |= (1 << (reg + 16));
+
+ etm_config.etm_addr_access_type[reg] = 0x9C;
+}
+
+static void setup_access_type(uint32_t reg, uint32_t value)
+{
+ etm_config.etm_addr_access_type[reg] &= 0xFFFFFFF8;
+ value &= 0x7;
+ etm_config.etm_addr_access_type[reg] |= value;
+}
+
+static void reset_filter(void)
+{
+ etm_config.etm_00_control = 0x0000D84E;
+ /* etm_02_trigger_event 0x00000000: address comparator 0 matches */
+ etm_config.etm_02_trigger_event = 0x00000000;
+ etm_config.etm_06_te_start_stop = 0x00000000;
+ etm_config.etm_07_te_single_addr_comp = 0x00000000;
+ /* etm_08_te_event 0x0000006F: always true */
+ etm_config.etm_08_te_event = 0x0000006F;
+ /* etm_09_te_control 0x01000000: exclude none */
+ etm_config.etm_09_te_control = 0x01000000;
+ etm_config.etm_0a_fifofull_region = 0x00000000;
+ etm_config.etm_0b_fifofull_level = 0x00000000;
+ /* etm_0c_vd_event 0x0000006F: always true */
+ etm_config.etm_0c_vd_event = 0x0000006F;
+ etm_config.etm_0d_vd_single_addr_comp = 0x00000000;
+ etm_config.etm_0e_vd_mmd = 0x00000000;
+ /* etm_0f_vd_control 0x00010000: exclude none */
+ etm_config.etm_0f_vd_control = 0x00010000;
+ etm_config.etm_addr_comp_value[0] = 0x00000000;
+ etm_config.etm_addr_comp_value[1] = 0x00000000;
+ etm_config.etm_addr_comp_value[2] = 0x00000000;
+ etm_config.etm_addr_comp_value[3] = 0x00000000;
+ etm_config.etm_addr_comp_value[4] = 0x00000000;
+ etm_config.etm_addr_comp_value[5] = 0x00000000;
+ etm_config.etm_addr_comp_value[6] = 0x00000000;
+ etm_config.etm_addr_comp_value[7] = 0x00000000;
+ etm_config.etm_addr_access_type[0] = 0x00000000;
+ etm_config.etm_addr_access_type[1] = 0x00000000;
+ etm_config.etm_addr_access_type[2] = 0x00000000;
+ etm_config.etm_addr_access_type[3] = 0x00000000;
+ etm_config.etm_addr_access_type[4] = 0x00000000;
+ etm_config.etm_addr_access_type[5] = 0x00000000;
+ etm_config.etm_addr_access_type[6] = 0x00000000;
+ etm_config.etm_addr_access_type[7] = 0x00000000;
+ etm_config.etm_data_comp_value[0] = 0x00000000;
+ etm_config.etm_data_comp_value[1] = 0x00000000;
+ etm_config.etm_data_comp_mask[0] = 0x00000000;
+ etm_config.etm_data_comp_mask[1] = 0x00000000;
+ etm_config.etm_counter_reload_value[0] = 0x00000000;
+ etm_config.etm_counter_reload_value[1] = 0x00000000;
+ etm_config.etm_counter_enable[0] = 0x0002406F;
+ etm_config.etm_counter_enable[1] = 0x0002406F;
+ etm_config.etm_counter_reload_event[0] = 0x0000406F;
+ etm_config.etm_counter_reload_event[1] = 0x0000406F;
+ etm_config.etm_60_seq_event_1_to_2 = 0x0000406F;
+ etm_config.etm_61_seq_event_2_to_1 = 0x0000406F;
+ etm_config.etm_62_seq_event_2_to_3 = 0x0000406F;
+ etm_config.etm_63_seq_event_3_to_1 = 0x0000406F;
+ etm_config.etm_64_seq_event_3_to_2 = 0x0000406F;
+ etm_config.etm_65_seq_event_1_to_3 = 0x0000406F;
+ etm_config.etm_6c_cid_comp_value_1 = 0x00000000;
+ etm_config.etm_6f_cid_comp_mask = 0x00000000;
+ etm_config.etm_78_sync_freq = 0x00000400;
+ etm_config.swconfig = 0x00000002;
+ /* etb_trig_cnt 0x00000020: ignore trigger */
+ etm_config.etb_trig_cnt = 0x00000000;
+ /* etb_init_ptr 0x00000010: 16 marker bytes */
+ etm_config.etb_init_ptr = 0x00000010;
+}
+
+#define MAX_COMMAND_STRLEN 40
+static ssize_t etm_dev_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ char command[MAX_COMMAND_STRLEN];
+ int strlen;
+ unsigned long value;
+ unsigned long reg1, reg2;
+ unsigned long addr1, addr2;
+
+ strlen = strnlen_user(data, MAX_COMMAND_STRLEN);
+ pr_debug("etm: string length: %d", strlen);
+ if (strlen == 0 || strlen == (MAX_COMMAND_STRLEN+1)) {
+ pr_err("etm: error in strlen: %d", strlen);
+ return -EFAULT;
+ }
+ /* includes the null character */
+ if (copy_from_user(command, data, strlen)) {
+ pr_err("etm: error in copy_from_user: %d", strlen);
+ return -EFAULT;
+ }
+
+ pr_debug("etm: input = %s", command);
+
+ switch (command[0]) {
+ case '0':
+ if (trace_enabled) {
+ disable_trace();
+ pr_info("etm: tracing disabled\n");
+ }
+ break;
+ case '1':
+ if (!trace_enabled) {
+ enable_trace();
+ pr_info("etm: tracing enabled\n");
+ }
+ break;
+ case 'f':
+ switch (command[2]) {
+ case 'i':
+ case 'd':
+ switch (command[4]) {
+ case 'i':
+ if (sscanf(&command[6], "%lx:%lx:%lx:%lx\\0",
+ ®1, &addr1, ®2, &addr2) != 4)
+ goto err_out;
+ if (reg1 > 7 || reg2 > 7 || (reg1 % 2))
+ goto err_out;
+ setup_range_filter(command[2], 'i',
+ reg1, addr1, reg2, addr2);
+ break;
+ case 'e':
+ if (sscanf(&command[6], "%lx:%lx:%lx:%lx\\0",
+ ®1, &addr1, ®2, &addr2) != 4)
+ goto err_out;
+ if (reg1 > 7 || reg2 > 7 || (reg1 % 2)
+ || command[2] == 'd')
+ goto err_out;
+ setup_range_filter(command[2], 'e',
+ reg1, addr1, reg2, addr2);
+ break;
+ case 's':
+ if (sscanf(&command[6], "%lx:%lx\\0",
+ ®1, &addr1) != 2)
+ goto err_out;
+ if (reg1 > 7)
+ goto err_out;
+ setup_start_stop_filter(command[2], 's',
+ reg1, addr1);
+ break;
+ case 't':
+ if (sscanf(&command[6], "%lx:%lx\\0",
+ ®1, &addr1) != 2)
+ goto err_out;
+ if (reg1 > 7)
+ goto err_out;
+ setup_start_stop_filter(command[2], 't',
+ reg1, addr1);
+ break;
+ default:
+ goto err_out;
+ }
+ break;
+ case 'r':
+ reset_filter();
+ break;
+ default:
+ goto err_out;
+ }
+ break;
+ case 'v':
+ switch (command[2]) {
+ case 'd':
+ switch (command[4]) {
+ case 'i':
+ if (sscanf(&command[6], "%lx:%lx:%lx:%lx\\0",
+ ®1, &addr1, ®2, &addr2) != 4)
+ goto err_out;
+ if (reg1 > 7 || reg2 > 7 || (reg1 % 2))
+ goto err_out;
+ setup_viewdata_range_filter('i',
+ reg1, addr1, reg2, addr2);
+ break;
+ case 'e':
+ if (sscanf(&command[6], "%lx:%lx:%lx:%lx\\0",
+ ®1, &addr1, ®2, &addr2) != 4)
+ goto err_out;
+ if (reg1 > 7 || reg2 > 7 || (reg1 % 2))
+ goto err_out;
+ setup_viewdata_range_filter('e',
+ reg1, addr1, reg2, addr2);
+ break;
+ case 's':
+ if (sscanf(&command[6], "%lx:%lx\\0",
+ ®1, &addr1) != 2)
+ goto err_out;
+ if (reg1 > 7)
+ goto err_out;
+ setup_viewdata_start_stop_filter('s',
+ reg1, addr1);
+ break;
+ case 't':
+ if (sscanf(&command[6], "%lx:%lx\\0",
+ ®1, &addr1) != 2)
+ goto err_out;
+ if (reg1 > 7)
+ goto err_out;
+ setup_viewdata_start_stop_filter('t',
+ reg1, addr1);
+ break;
+ default:
+ goto err_out;
+ }
+ break;
+ default:
+ goto err_out;
+ }
+ break;
+ case 'a':
+ switch (command[2]) {
+ case 't':
+ if (sscanf(&command[4], "%lx:%lx\\0",
+ ®1, &value) != 2)
+ goto err_out;
+ if (reg1 > 7 || value > 6)
+ goto err_out;
+ setup_access_type(reg1, value);
+ break;
+ default:
+ goto err_out;
+ }
+ break;
+ default:
+ goto err_out;
+ }
+
+ return len;
+
+err_out:
+ return -EFAULT;
+}
+
+static int etm_dev_release(struct inode *inode, struct file *file)
+{
+ if (cpu_to_dump == next_cpu_to_dump)
+ next_cpu_to_dump = 0;
+ cpu_to_dump = next_cpu_to_dump;
+
+ atomic_set(&etm_dev_in_use, 0);
+ pr_debug("%s: released\n", __func__);
+ return 0;
+}
+
+static const struct file_operations etm_dev_fops = {
+ .owner = THIS_MODULE,
+ .open = etm_dev_open,
+ .read = etm_dev_read,
+ .write = etm_dev_write,
+ .release = etm_dev_release,
+};
+
+static struct miscdevice etm_dev = {
+ .name = "msm_etm",
+ .minor = MISC_DYNAMIC_MINOR,
+ .fops = &etm_dev_fops,
+};
+
+static void __cpu_clear_sticky(void *unused)
+{
+ etm_read(ETMPDSR); /* clear sticky bit in PDSR */
+ isb();
+}
+
+static int __init etm_init(void)
+{
+ int ret, cpu;
+
+ ret = misc_register(&etm_dev);
+ if (ret)
+ return -ENODEV;
+
+ alloc_b = alloc_percpu(typeof(*alloc_b));
+ if (!alloc_b)
+ goto err1;
+
+ for_each_possible_cpu(cpu)
+ *per_cpu_ptr(alloc_b, cpu) = &buf[cpu];
+
+ wake_lock_init(&etm_wake_lock, WAKE_LOCK_SUSPEND, "msm_etm");
+ pm_qos_add_request(&etm_qos_req, PM_QOS_CPU_DMA_LATENCY,
+ PM_QOS_DEFAULT_VALUE);
+
+ /* No need to explicity turn on ETM clock since CP14 access go
+ * through via the autoclock turn on/off
+ */
+ __cpu_clear_sticky(NULL);
+ smp_call_function(__cpu_clear_sticky, NULL, 1);
+
+ cpu_to_dump = next_cpu_to_dump = 0;
+
+ pr_info("ETM/ETB intialized.\n");
+
+ if (trace_on_boot)
+ enable_trace();
+
+ return 0;
+
+err1:
+ misc_deregister(&etm_dev);
+ return -ENOMEM;
+}
+module_init(etm_init);
+
+static void __exit etm_exit(void)
+{
+ disable_trace();
+ pm_qos_remove_request(&etm_qos_req);
+ wake_lock_destroy(&etm_wake_lock);
+ free_percpu(alloc_b);
+ misc_deregister(&etm_dev);
+}
+module_exit(etm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("embedded trace driver");