summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/trace/stm-trace.txt193
-rw-r--r--arch/arm/Kconfig.debug9
-rw-r--r--arch/arm/common/Makefile1
-rw-r--r--arch/arm/common/boottime.c46
-rw-r--r--arch/arm/include/asm/setup.h21
-rw-r--r--arch/arm/mach-ux500/board-mop500-stm.c203
-rw-r--r--arch/arm/mach-ux500/hwreg.c651
-rw-r--r--drivers/misc/Kconfig1
-rw-r--r--drivers/misc/Kconfig.stm114
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/stm.c797
-rw-r--r--include/Kbuild1
-rw-r--r--include/linux/boottime.h89
-rw-r--r--include/trace/Kbuild1
-rw-r--r--include/trace/stm.h223
-rw-r--r--init/Kconfig9
-rw-r--r--init/Makefile1
-rw-r--r--init/boottime.c467
-rw-r--r--init/main.c6
-rw-r--r--kernel/printk.c12
-rw-r--r--kernel/trace/trace.c11
-rw-r--r--kernel/trace/trace_events.c3
-rw-r--r--kernel/trace/trace_sched_switch.c10
23 files changed, 2869 insertions, 1 deletions
diff --git a/Documentation/trace/stm-trace.txt b/Documentation/trace/stm-trace.txt
new file mode 100644
index 00000000000..cd73c2b87b7
--- /dev/null
+++ b/Documentation/trace/stm-trace.txt
@@ -0,0 +1,193 @@
+ MIPI System Trace Module driver
+ ===============================
+
+Copyright (C) ST-Ericsson SA 2011
+ Authors: Pierre Peiffer <pierre dot peiffer at stericsson dot com>
+ Philippe Langlais <philippe dot langlais at linaro dot org>
+ License: The GNU Free Documentation License, Version 1.2
+ (dual licensed under the GPL v2)
+
+Hardware overview
+=================
+ This hardware collects and provides simple tracepoints,
+ so a system processor (in our case the main ARM CPU,
+ or some small CPUs and DSPs) can write some data,
+ up to 8 bytes, into a register and out comes a log entry
+ with a time stamp (20ns resolution) on one of 256 channels. Also
+ hardware tracepoints are supported.
+
+ This module external interface is a pad on the chip
+ which complies to the MIPI System Trace Protocol v1.0
+ (see http://www.mipi.org/specifications/debug)
+ and the actual trace output can be read by an
+ electronic probe, not by software so it cannot be intercepted by
+ the CPU and reach Linux userspace.
+
+ Bandwidth depends on number of lines & bus frequency (for example on ux500
+ SoC 4 lines at max 100MHz eg max 400Mbit/s shared between 7 cores).
+ Transmit FIFO size: 256 samples up to 8 bytes.
+ On ux500 platform there is 2 contiguous STM blocks (eg 512 channels)
+
+Software Overview
+=================
+ Write atomicity and write order on STM trace channels is ensured by the fact
+ we try to allocate one channel by execution thread (no concurrent access).
+ There is 2 modes one lossless but intrusive aka Software mode and
+ another lossy mode less intrusive aka Hardware mode, by default
+ all sources are configured in Hardware mode and enabled.
+ The end of data packet is marked by a time stamp on latest byte(s) only.
+
+Kernel API
+----------
+ Configuration functions:
+ output trace clock frequency, trace mode, output port configuration
+ and enable/disable STM trace sources
+ Expose a debugfs interface too for STM trace control
+
+ Alloc/free STM trace channel functions
+
+ Set of low level atomic trace functions for 1, 2, 4 or 8 bytes
+ with & w/o time stamp
+
+ Higher level lockless trace functions:
+ stm_trace_buffer:
+ allocate a channel in 128 highest channels available
+ output the trace buffer with arbitrary length
+ (latest byte(s) automatically time stamped) then free the channel
+ stm_trace_buffer_onchannel:
+ use given channel to output the trace buffer
+ with arbitrary length (latest byte(s) automatically time stamped)
+
+ File IO output console like interface (open, close, write)
+
+ See <trace/stm.h> & drivers/misc/stm.c for more detail
+
+debugfs API
+-----------
+clockdiv:
+ This is used to set or display the current clock divisor
+ that is configured
+
+connection:
+ This is used to set or display the current output connector
+ that is configured (common values, 0 not connected, 1 for default
+ connection, 3 on Ux500 for APE MIPI34 connection)
+
+free_channels:
+ This is used to display the total number of free channels
+
+masters_enable:
+ This sets or displays whether the STM trace sources
+ are activated. Each bits represent the state of corresponding source:
+ 0 for disable or 1 to enable it.
+
+masters_modes:
+ This sets or displays the STM trace sources modes.
+ Each bits represent the mode of corresponding source:
+ 0 for Sofware lossless mode or 1 for Hardware lossy mode.
+
+User API
+--------
+ IOCTLs or debugfs for controls
+ 2 levels API for tracing:
+ - Standard write function, in this case a channel is automatically
+ allocated at first write, after you can channel number
+ with IOCTL STM_GET_CHANNEL_NO
+ - mmap for direct access of all STM trace channels port plus
+ a set of IOCTLs for alloc/free channels, in this case you can
+ write your own lib to easiest its usage
+
+Examples of using the STM
+=========================
+First mount debugfs with:
+mount -t debugfs none /sys/kernel/debug
+
+In a shell scipt
+----------------
+It's as easy as:
+ echo "My trace point" > /dev/stm
+
+To avoid trace overflow, increase STM clock by decreasing the clockdiv with:
+ echo 1 >/sys/kernel/debug/stm/clockdiv # now use DIV2 instead of default DIV8
+If not enough you can disable some sources with:
+ echo YourEnableSources > /sys/kernel/debug/stm/masters_enable
+If always not enough the ultime intrusive way is to change the sources mode
+and set the corresponding sources in Software mode (set corresponding source
+bit to 0) with:
+ echo YourModeSources > /sys/kernel/debug/stm/masters_modes
+ (be aware some source doesn't support Software mode => keep it in HW mode)
+
+NB: on Ux500 platform, first you have to configure STM output port to switch
+APE tracing on MIPI34 connector with:
+ echo 3 > /sys/kernel/debug/stm/connection
+
+In C language
+-------------
+
+The easy way more intrusive (with STM buffer recopy):
+
+#include <trace/stm.h>
+
+int fd, i;
+char buf[1024]; // Try to align this buffer on 64 bits if possible
+
+ fd = open("/dev/stm", O_WRONLY);
+ snprintf(buf, 1024, "STM0 Hello world\n");
+ write(fd, buf, strlen(buf));
+ ioctl(fd, STM_GET_CHANNEL_NO, &i);
+ snprintf(buf, 1024, "Use channel #%d\n", i);
+ write(fd, buf, strlen(buf));
+ close(fd);
+
+NB: You can call open("/dev/stm", O_WRONLY) as many times as necessary
+to allocate a different channel to avoid concurrency in your
+multithreaded application.
+
+The more efficient way, use mmap'ed STM channels memory (to put in a lib):
+
+#include <trace/stm.h>
+
+int fd, i, c, l, maxChannels;
+char buf[1024]; // Try to align this buffer on 64 bits if possible
+volatile struct stm_channel *channels; // mmap'ed channels area
+
+ fd = open("/dev/stm", O_RDWR);
+ ioctl(fd0, STM_GET_NB_MAX_CHANNELS, &maxChannels);
+ channels = (struct stm_channel *)mmap(0, maxChannels*sizeof(*channels),
+ PROT_WRITE, MAP_SHARED, fd, 0);
+ assert(channels != MAP_FAILED);
+
+ if (!ioctl(fd, STM_GET_FREE_CHANNEL, &c)) {
+ l = snprintf(buf, 1024, "STM0 Hello world on channel #%d\n", c);
+ // lazy implementation you have to send buffer by 8 Bytes when possible
+ // and be sure you don't share this channel with others threads
+ for (i=0; i<l; i++) {
+ channels[c].stamp8 = buf[i];
+ }
+ ioctl(fd, STM_RELEASE_CHANNEL, c);
+ }
+ munmap((void *)channels, maxChannels*sizeof(*channels));
+ close(fd);
+
+Kernel Internal usages
+======================
+Dynamically channels dedicated for the kernel are allocated
+in the 128 highest ones
+
+Via menuconfig you can:
+- Duplicate printk output on a STM dedicated channel (255)
+- Have realtime ftrace output to a STM dedicated channel (254),
+ if corresponding TRACER is enabled
+- Have realtime sched context switch & sched wakeup output on dedicated channels
+ (253, 252), if corresponding TRACER is enabled
+- Have Stack Trace on dedicated channels (251)
+- Duplicate trace_printk output on dedicated channels (250 & 249)
+
+
+And in the future:
+------------------
+- Use it in standard kernel tracing infrastucture,
+ possibilities:
+ - Insert other STM trace calls before trace ring buffer write
+ - Substitute time stamping & trace ring buffer by STM
+
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index c5213e78606..6c44e08b6ef 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -257,6 +257,15 @@ config EARLY_PRINTK
kernel low-level debugging functions. Add earlyprintk to your
kernel parameters to enable this console.
+config PRINTK_LL
+ bool "Use printascii in printk"
+ depends on DEBUG_LL
+ help
+ Say Y here if you want to have printk send its output via the
+ kernel low-level debugging functions. This is useful if you
+ are debugging code that executes before the earlyprintk console
+ is initialized.
+
config OC_ETM
bool "On-chip ETM and ETB"
depends on ARM_AMBA
diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile
index 6ea9b6f3607..86d67017c1e 100644
--- a/arch/arm/common/Makefile
+++ b/arch/arm/common/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_ARCH_IXP2000) += uengine.o
obj-$(CONFIG_ARCH_IXP23XX) += uengine.o
obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o
obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o
+obj-$(CONFIG_BOOTTIME) += boottime.o
diff --git a/arch/arm/common/boottime.c b/arch/arm/common/boottime.c
new file mode 100644
index 00000000000..73e9e04ed37
--- /dev/null
+++ b/arch/arm/common/boottime.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2009-2010
+ *
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * Store boot times measured during for example u-boot startup.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/boottime.h>
+#include <linux/string.h>
+#include <asm/setup.h>
+
+static u32 bootloader_idle;
+static u32 bootloader_total;
+
+static int __init boottime_parse_tag(const struct tag *tag)
+{
+ int i;
+ char buff[BOOTTIME_MAX_NAME_LEN];
+
+ bootloader_idle = tag->u.boottime.idle;
+ bootloader_total = tag->u.boottime.total;
+
+ for (i = 0; i < tag->u.boottime.num; i++) {
+ snprintf(buff, BOOTTIME_MAX_NAME_LEN, "%s+0x0/0x0",
+ tag->u.boottime.entry[i].name);
+ buff[BOOTTIME_MAX_NAME_LEN - 1] = '\0';
+ boottime_mark_wtime(buff, tag->u.boottime.entry[i].time);
+ }
+
+ return 0;
+}
+
+__tagtable(ATAG_BOOTTIME, boottime_parse_tag);
+
+int boottime_bootloader_idle(void)
+{
+ if (bootloader_total == 0)
+ return 0;
+
+ return (int) ((bootloader_idle) / (bootloader_total / 100));
+}
diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h
index 915696dd9c7..ad937bc348f 100644
--- a/arch/arm/include/asm/setup.h
+++ b/arch/arm/include/asm/setup.h
@@ -143,6 +143,23 @@ struct tag_memclk {
__u32 fmemclk;
};
+/* for automatic boot timing testcases */
+#define ATAG_BOOTTIME 0x41000403
+#define BOOTTIME_MAX_NAME_LEN 64
+#define BOOTTIME_MAX 10
+
+struct boottime_entry {
+ u32 time; /* in us */
+ u8 name[BOOTTIME_MAX_NAME_LEN];
+};
+
+struct tag_boottime {
+ struct boottime_entry entry[BOOTTIME_MAX];
+ u32 idle; /* in us */
+ u32 total; /* in us */
+ u8 num;
+};
+
struct tag {
struct tag_header hdr;
union {
@@ -165,6 +182,10 @@ struct tag {
* DC21285 specific
*/
struct tag_memclk memclk;
+ /*
+ * Boot time
+ */
+ struct tag_boottime boottime;
} u;
};
diff --git a/arch/arm/mach-ux500/board-mop500-stm.c b/arch/arm/mach-ux500/board-mop500-stm.c
new file mode 100644
index 00000000000..7d8148c5878
--- /dev/null
+++ b/arch/arm/mach-ux500/board-mop500-stm.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2011 ST-Ericsson
+ *
+ * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson.
+ * Author: Olivier Germain <olivier.germain@stericsson.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/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/gpio/nomadik.h>
+#include <plat/pincfg.h>
+#include <mach/devices.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <asm/io.h>
+#include <trace/stm.h>
+#include "pins-db8500.h"
+
+static pin_cfg_t mop500_stm_mipi34_pins[] = {
+ GPIO70_STMAPE_CLK | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+ GPIO71_STMAPE_DAT3 | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+ GPIO72_STMAPE_DAT2 | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+ GPIO73_STMAPE_DAT1 | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+ GPIO74_STMAPE_DAT0 | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+ GPIO75_U2_RXD | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+ GPIO76_U2_TXD | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+};
+
+static pin_cfg_t mop500_stm_mipi60_pins[] = {
+ GPIO153_U2_RXD,
+ GPIO154_U2_TXD,
+ GPIO155_STMAPE_CLK,
+ GPIO156_STMAPE_DAT3,
+ GPIO157_STMAPE_DAT2,
+ GPIO158_STMAPE_DAT1,
+ GPIO159_STMAPE_DAT0,
+};
+
+static pin_cfg_t mop500_ske_pins[] = {
+ GPIO153_KP_I7 | PIN_INPUT_PULLDOWN | PIN_SLPM_INPUT_PULLUP,
+ GPIO154_KP_I6 | PIN_INPUT_PULLDOWN | PIN_SLPM_INPUT_PULLUP,
+ GPIO155_KP_I5 | PIN_INPUT_PULLDOWN | PIN_SLPM_INPUT_PULLUP,
+ GPIO156_KP_I4 | PIN_INPUT_PULLDOWN | PIN_SLPM_INPUT_PULLUP,
+ GPIO161_KP_I3 | PIN_INPUT_PULLDOWN | PIN_SLPM_INPUT_PULLUP,
+ GPIO162_KP_I2 | PIN_INPUT_PULLDOWN | PIN_SLPM_INPUT_PULLUP,
+ GPIO163_KP_I1 | PIN_INPUT_PULLDOWN | PIN_SLPM_INPUT_PULLUP,
+ GPIO164_KP_I0 | PIN_INPUT_PULLDOWN | PIN_SLPM_INPUT_PULLUP,
+ GPIO157_KP_O7 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+ GPIO158_KP_O6 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+ GPIO159_KP_O5 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+ GPIO160_KP_O4 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+ GPIO165_KP_O3 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+ GPIO166_KP_O2 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+ GPIO167_KP_O1 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+ GPIO168_KP_O0 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+};
+
+static int stm_ste_disable_ape_on_mipi60(void)
+{
+ int retval;
+
+ retval = nmk_config_pins_sleep(ARRAY_AND_SIZE(mop500_stm_mipi60_pins));
+ if (retval)
+ pr_err("STM: Failed to disable MIPI60\n");
+ else {
+ retval = nmk_config_pins(ARRAY_AND_SIZE(mop500_ske_pins));
+ if (retval)
+ pr_err("STM: Failed to enable SKE gpio\n");
+ }
+ return retval;
+}
+
+/*
+ * Manage STM output pins connection (MIP34/MIPI60 connectors)
+ */
+#define PRCM_GPIOCR (_PRCMU_BASE + 0x138)
+#define PRCM_GPIOCR_DBG_STM_MOD_CMD1 0x800
+#define PRCM_GPIOCR_DBG_UARTMOD_CMD0 0x1
+
+
+static int stm_ste_connection(enum stm_connection_type con_type)
+{
+ int retval = -EINVAL;
+ u32 gpiocr = readl(PRCM_GPIOCR);
+
+ if (con_type != STM_DISCONNECT) {
+ /* Always enable MIPI34 GPIO pins */
+ retval = nmk_config_pins(
+ ARRAY_AND_SIZE(mop500_stm_mipi34_pins));
+ if (retval) {
+ pr_err("STM: Failed to enable MIPI34\n");
+ return retval;
+ }
+ }
+
+ switch (con_type) {
+ case STM_DEFAULT_CONNECTION:
+ case STM_STE_MODEM_ON_MIPI34_NONE_ON_MIPI60:
+ /* Enable altC3 on GPIO70-74 (STMMOD) & GPIO75-76 (UARTMOD) */
+ gpiocr |= (PRCM_GPIOCR_DBG_STM_MOD_CMD1
+ | PRCM_GPIOCR_DBG_UARTMOD_CMD0);
+ writel(gpiocr, PRCM_GPIOCR);
+ retval = stm_ste_disable_ape_on_mipi60();
+ break;
+
+ case STM_STE_APE_ON_MIPI34_NONE_ON_MIPI60:
+ /* Disable altC3 on GPIO70-74 (STMMOD) & GPIO75-76 (UARTMOD) */
+ gpiocr &= ~(PRCM_GPIOCR_DBG_STM_MOD_CMD1
+ | PRCM_GPIOCR_DBG_UARTMOD_CMD0);
+ writel(gpiocr, PRCM_GPIOCR);
+ retval = stm_ste_disable_ape_on_mipi60();
+ break;
+
+ case STM_STE_MODEM_ON_MIPI34_APE_ON_MIPI60:
+ /* Enable altC3 on GPIO70-74 (STMMOD) and GPIO75-76 (UARTMOD) */
+ gpiocr |= (PRCM_GPIOCR_DBG_STM_MOD_CMD1
+ | PRCM_GPIOCR_DBG_UARTMOD_CMD0);
+ writel(gpiocr, PRCM_GPIOCR);
+
+ /* Enable APE on MIPI60 */
+ retval = nmk_config_pins_sleep(ARRAY_AND_SIZE(mop500_ske_pins));
+ if (retval)
+ pr_err("STM: Failed to disable SKE GPIO\n");
+ else {
+ retval = nmk_config_pins(
+ ARRAY_AND_SIZE(mop500_stm_mipi60_pins));
+ if (retval)
+ pr_err("STM: Failed to enable MIPI60\n");
+ }
+ break;
+
+ case STM_DISCONNECT:
+ retval = nmk_config_pins_sleep(
+ ARRAY_AND_SIZE(mop500_stm_mipi34_pins));
+ if (retval)
+ pr_err("STM: Failed to disable MIPI34\n");
+
+ retval = stm_ste_disable_ape_on_mipi60();
+ break;
+
+ default:
+ pr_err("STM: bad connection type\n");
+ break;
+ }
+ return retval;
+}
+
+/* Possible STM sources (masters) on ux500 */
+enum stm_master {
+ STM_ARM0 = 0,
+ STM_ARM1 = 1,
+ STM_SVA = 2,
+ STM_SIA = 3,
+ STM_SIA_XP70 = 4,
+ STM_PRCMU = 5,
+ STM_MCSBAG = 9
+};
+
+#define STM_ENABLE_ARM0 BIT(STM_ARM0)
+#define STM_ENABLE_ARM1 BIT(STM_ARM1)
+#define STM_ENABLE_SVA BIT(STM_SVA)
+#define STM_ENABLE_SIA BIT(STM_SIA)
+#define STM_ENABLE_SIA_XP70 BIT(STM_SIA_XP70)
+#define STM_ENABLE_PRCMU BIT(STM_PRCMU)
+#define STM_ENABLE_MCSBAG BIT(STM_MCSBAG)
+
+/*
+ * These are the channels used by NMF and some external softwares
+ * expect the NMF traces to be output on these channels
+ * For legacy reason, we need to reserve them.
+ */
+static const s16 stm_channels_reserved[] = {
+ 100, /* NMF MPCEE channel */
+ 101, /* NMF CM channel */
+ 151, /* NMF HOSTEE channel */
+};
+
+/* On Ux500 we 2 consecutive STMs therefore 512 channels available */
+static struct stm_platform_data stm_pdata = {
+ .regs_phys_base = U8500_STM_REG_BASE,
+ .channels_phys_base = U8500_STM_BASE,
+ .id_mask = 0x000fffff, /* Ignore revisions differences */
+ .channels_reserved = stm_channels_reserved,
+ .channels_reserved_sz = ARRAY_SIZE(stm_channels_reserved),
+ /* Enable all except MCSBAG */
+ .masters_enabled = STM_ENABLE_ARM0 | STM_ENABLE_ARM1 |
+ STM_ENABLE_SVA | STM_ENABLE_PRCMU |
+ STM_ENABLE_SIA | STM_ENABLE_SIA_XP70,
+ /* Provide function for MIPI34/MIPI60 STM connection */
+ .stm_connection = stm_ste_connection,
+};
+
+struct platform_device u8500_stm_device = {
+ .name = "stm",
+ .id = -1,
+ .dev = {
+ .platform_data = &stm_pdata,
+ },
+};
diff --git a/arch/arm/mach-ux500/hwreg.c b/arch/arm/mach-ux500/hwreg.c
new file mode 100644
index 00000000000..1a47e60ed46
--- /dev/null
+++ b/arch/arm/mach-ux500/hwreg.c
@@ -0,0 +1,651 @@
+/*
+ * Copyright (C) 2011 ST-Ericsson SA
+ *
+ * Author: Etienne CARRIERE <etienne.carriere@stericsson.com> for ST-Ericsson
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * HWREG: debug purpose module to map declared IOs and read/write
+ * access from debugfs entries.
+ *
+ * HWREG 32bit DB8500 v2.0 register access
+ * =======================================
+ *
+ * 32bit read:
+ * # echo <addr> > <debugfs>/mem/reg-addr
+ * # cat <debugfs>/mem/reg-val
+ *
+ * 32bit write:
+ * # echo <addr> > <debugfs>/mem/reg-addr
+ * # echo <value> > <debugfs>/mem/reg-val
+ *
+ * <addr> 0x-prefixed hexadecimal
+ * <value> decimal or 0x-prefixed hexadecimal
+ *
+ * HWREG DB8500 formated read/write access
+ * =======================================
+ *
+ * Read: read data, data>>SHIFT, data&=MASK, output data
+ * [0xABCDEF98] shift=12 mask=0xFFF => 0x00000CDE
+ * Write: read data, data &= ~(MASK<<SHIFT), data |= (VALUE<<SHIFT), write data
+ * [0xABCDEF98] shift=12 mask=0xFFF value=0x123 => [0xAB123F98]
+ *
+ * Usage:
+ * # echo "CMD [OPTIONS] ADRESS [VALUE]" > $debugfs/mem/hwreg
+ *
+ * CMD read read access
+ * write write access
+ *
+ * ADDRESS target reg physical addr (0x-hexa)
+ *
+ * VALUE (write) value to be updated
+ *
+ * OPTIONS
+ * -d|-dec (read) output in decimal
+ * -h|-hexa (read) output in 0x-hexa (default)
+ * -l|-w|-b 32bit (default), 16bit or 8bit reg access
+ * -m|-mask MASK 0x-hexa mask (default 0xFFFFFFFF)
+ * -s|-shift SHIFT bit shift value (read:left, write:right)
+ * -o|-offset OFFSET address offset to add to ADDRESS value
+ *
+ * Warning: bit shift operation is applied to bit-mask.
+ * Warning: bit shift direction depends on read or right command.
+ *
+ * Examples:
+ *
+ * before: [*ADDRESS = 0xABCDEF98]
+ * # echo read -h -mask 0xFFF -shift 12 ADDRESS > hwreg
+ * # cat hwreg-shift
+ * 0x0000CDE
+ * # echo write -h -mask 0xFFF -shift 12 ADDRESS 0x123 > hwreg
+ * # cat hwreg-shift
+ * 0x0000123
+ * after [*ADDRESS = 0xAB123F98]
+ *
+ * before: [*ADDRESS = 0xABCDEF98]
+ * # echo read -h -mask 0x00F0F000 ADDRESS 0x12345678 > hwreg
+ * # cat hwreg-shift
+ * 0x00C0E000
+ * # echo write -h -mask 0x00F0F000 ADDRESS 0x12345678 > hwreg
+ * # cat hwreg-shift
+ * 0xAB3D5F98
+ * after [*ADDRESS = 0xAB123F98]
+ *
+ * Read DB8500 version (full ID, chip version ID, chip version ID):
+ *
+ * echo read 0x9001DBF4 > hwreg
+ * cat hwreg
+ * echo read -m 0xFFFF -s 8 0x9001DBF4 > hwreg
+ * cat hwreg
+ * echo read -m 0xFF -s 0 0x9001DBF4 > hwreg
+ * cat hwreg
+ *
+ * Read and Enable/Disable I2C PRCMU clock:
+ *
+ * printf "I2CCLK = " && echo read -m 1 -s 8 0x80157520 > hwreg
+ * cat /sys/kernel/debug/db8500/hwreg
+ * printf "I2CCLK off" && echo write -m 1 -s 8 0x80157518 1 > hwreg
+ * printf "I2CCLK on" && echo write -m 1 -s 8 0x80157510 1 > hwreg
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#include <mach/hardware.h>
+
+/*
+ * temporary definitions
+ * The following declarations are to be removed
+ * when kernel/arch/arm/mach-ux8500/include/mach/db8500-regs.h is up-to-date
+ */
+
+/* DDR-SDRAM chip-select 0 (0x0000 0000 : 0x1FFF FFFF) */
+#ifndef U8500_SCU_CD_R4_BASE
+#define U8500_SCU_CD_R4_BASE 0x17c40000
+#endif
+
+#ifndef U8500_SCU_AD_R4_BASE
+#define U8500_SCU_AD_R4_BASE 0x17d40000
+#endif
+
+#ifndef U8500_HSI2CMODEMR4_BASE
+#define U8500_HSI2CMODEMR4_BASE 0x17e02000
+#endif
+/* End of temporary definitions */
+
+static struct dentry *hwreg_debugfs_dir;
+
+/* 32bit read/write ressources */
+static u32 debug_address; /* shared: single read/write access */
+
+/* hwreg entry ressources */
+struct hwreg_cfg {
+ uint addr; /* target physical addr to access */
+ uint fmt; /* format */
+ uint mask; /* read/write mask, applied before any bit shift */
+ int shift; /* bit shift (read:right shift, write:left shift */
+};
+#define REG_FMT_DEC(c) ((c)->fmt & 0x1) /* bit 0: 0=hexa, 1=dec */
+#define REG_FMT_HEX(c) (!REG_FMT_DEC(c)) /* bit 0: 0=hexa, 1=dec */
+#define REG_FMT_32B(c) (((c)->fmt & 0x6)==0x0) /* bit[2:1]=0 => 32b access */
+#define REG_FMT_16B(c) (((c)->fmt & 0x6)==0x2) /* bit[2:1]=1 => 16b access */
+#define REG_FMT_8B(c) (((c)->fmt & 0x6)==0x4) /* bit[2:1]=2 => 8b access */
+
+static struct hwreg_cfg hwreg_cfg = {
+ .addr = 0, /* default: invalid phys addr */
+ .fmt = 0, /* default: 32bit access, hex output */
+ .mask = 0xFFFFFFFF, /* default: no mask */
+ .shift = 0, /* default: no bit shift */
+};
+
+/* HWREG guts: mapping table */
+
+struct hwreg_io_range {
+ u32 base;
+ u32 size;
+ u8 *addr;
+};
+
+/*
+ * HWREG guts: mapping table
+ */
+static struct hwreg_io_range hwreg_io_map[] = {
+ /* Periph1 Peripherals */
+ {.base = U8500_PER1_BASE, .size = 0x10000},
+ /* Periph2 Peripherals */
+ {.base = U8500_PER2_BASE, .size = 0x10000},
+ /* Periph3 Peripherals */
+ {.base = U8500_PER3_BASE, .size = 0x10000},
+ /* Periph4 Peripherals */
+ {.base = U8500_PER4_BASE, .size = 0x70000},
+ /* Periph5 Periphals */
+ {.base = U8500_PER5_BASE, .size = 0x20000},
+ /* Periph6 Peripherals */
+ {.base = U8500_PER6_BASE, .size = 0x10000},
+ /*
+ * Snoop Control Unit, A9 Private interrupt IF,
+ * A9 private peripherals, Level-2 Cache Configuration registers,
+ * and some reserved area
+ */
+ {.base = U8500_SCU_BASE, .size = 0x4000},
+
+ /* DISPLAY Ctrl. configuration registers */
+ {.base = U8500_MCDE_BASE, .size = SZ_4K},
+
+ /* DSI1 link registers */
+ {.base = U8500_DSI_LINK1_BASE, .size = SZ_4K},
+
+ /* DSI2 link registers */
+ {.base = U8500_DSI_LINK2_BASE, .size = SZ_4K},
+
+ /* DSI3 link registers */
+ {.base = U8500_DSI_LINK3_BASE, .size = SZ_4K},
+
+ /* DMA Ctrl. configuration registers (base address changed in V1) */
+ {.base = U8500_DMA_BASE, .size = SZ_4K},
+
+ /* 0xB7A00000 -> 0xB7E04000: Modem I2C */
+ {.base = U8500_MODEM_I2C, .size = 0x404000},
+
+ /* 0xA0390000 -> 0xA039FFFF: SBAG configuration registers */
+ {.base = U8500_SBAG_BASE, .size = SZ_4K},
+
+ /* 0xA0300000 -> 0xA031FFFF: SGA configuration registers */
+ {.base = U8500_SGA_BASE, .size = 0x10000},
+
+ /* 0xA0200000 -> 0xA02FFFFF: Smart Imaging Acc. Data Memory space (SIA) */
+ {.base = U8500_SIA_BASE, .size = 0x60000},
+
+ /* 0xA0100000 -> 0xA01FFFFF: Smart Video Acc. Data Memory space (SVA) */
+ {.base = U8500_SVA_BASE, .size = 0x60000},
+
+ /* 0x81000000 -> 0x8103FFFF: Main ICN Crossbar configuration registers */
+ {.base = U8500_ICN_BASE, .size = 0x2000},
+
+ /* 0x80140000 -> 0x8014FFFF: HSEM (Semaphores) configuration */
+ {.base = U8500_HSEM_BASE, .size = SZ_4K},
+
+ /* 0x80130000 -> 0x8013FFFF: B2R2 configuration registers */
+ {.base = U8500_B2R2_BASE, .size = SZ_4K},
+
+ /* 0x80100000 -> 0x8010FFFF: STM */
+ {.base = U8500_STM_BASE, .size = 0x10000},
+
+ /* High part of embedded boot ROM */
+ {.base = U8500_ASIC_ID_BASE, .size = SZ_4K},
+
+ /* 0x17C4 0000 : 0x17C4 007C */
+ {.base = U8500_SCU_CD_R4_BASE, .size = SZ_4K},
+
+ /* 0x17D4 0000 : 0x17D4 041C */
+ {.base = U8500_SCU_AD_R4_BASE, .size = SZ_4K},
+
+ /* 0x17E0 2000 : 0x17E0 2FFC */
+ {.base = U8500_HSI2CMODEMR4_BASE, .size = SZ_4K},
+
+ {.base = 0, .size = 0, },
+
+};
+
+static void hwreg_io_init(void)
+{
+ int i;
+
+ for (i = 0; hwreg_io_map[i].base; ++i) {
+ hwreg_io_map[i].addr = ioremap(hwreg_io_map[i].base,
+ hwreg_io_map[i].size);
+ if (!hwreg_io_map[i].addr)
+ printk(KERN_WARNING
+ "%s: ioremap for %d (%08x) failed\n",
+ __func__, i, hwreg_io_map[i].base);
+ }
+}
+
+static void hwreg_io_exit(void)
+{
+ int i;
+
+ for (i = 0; hwreg_io_map[i].base; ++i)
+ if (hwreg_io_map[i].addr)
+ iounmap(hwreg_io_map[i].addr);
+}
+
+static void *hwreg_io_ptov(u32 phys)
+{
+ int i;
+
+ for (i = 0; hwreg_io_map[i].base; ++i) {
+ u32 base = hwreg_io_map[i].base;
+ u32 size = hwreg_io_map[i].size;
+ u8 *addr = hwreg_io_map[i].addr;
+
+ if (phys < base || phys >= base + size)
+ continue;
+
+ if (addr)
+ return addr + phys - base;
+
+ break;
+ }
+
+ return NULL;
+}
+
+
+/*
+ * HWREG 32bit DB8500 register read/write access debugfs part
+ */
+
+static int hwreg_address_print(struct seq_file *s, void *p)
+{
+ return seq_printf(s, "0x%08X\n", debug_address);
+}
+
+static int hwreg_address_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hwreg_address_print, inode->i_private);
+}
+
+static ssize_t hwreg_address_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ int err;
+ unsigned long user_address;
+
+ err = kstrtoul_from_user(user_buf, count, 0, &user_address);
+
+ if (err)
+ return err;
+
+ if (hwreg_io_ptov(user_address)==NULL)
+ return -EADDRNOTAVAIL;
+
+ debug_address = user_address;
+ return count;
+}
+
+static int hwreg_value_print(struct seq_file *s, void *p)
+{
+ void *ptr;
+
+ ptr = hwreg_io_ptov(debug_address);
+ if (ptr == NULL)
+ return -EADDRNOTAVAIL;
+ seq_printf(s, "0x%X\n", readl(ptr));
+ return 0;
+}
+
+static int hwreg_value_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hwreg_value_print, inode->i_private);
+}
+
+static ssize_t hwreg_value_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ int err;
+ unsigned long user_val;
+ void *ptr;
+
+ err = kstrtoul_from_user(user_buf, count, 0, &user_val);
+
+ if (err)
+ return err;
+
+ if ((ptr = hwreg_io_ptov(debug_address)) == NULL)
+ return -EFAULT;
+ writel(user_val, ptr);
+ return count;
+}
+
+static const struct file_operations hwreg_address_fops = {
+ .open = hwreg_address_open,
+ .write = hwreg_address_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+static const struct file_operations hwreg_value_fops = {
+ .open = hwreg_value_open,
+ .write = hwreg_value_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+/* 'map' read entry: display current HWREG IO mapping table */
+static int hwreg_map_print(struct seq_file *s, void *p)
+{
+ int err, i;
+ for (i = 0; hwreg_io_map[i].base; ++i) {
+ err = seq_printf(s, "%d: 0x%08X => 0x%08X\n",
+ i, hwreg_io_map[i].base,
+ hwreg_io_map[i].base+hwreg_io_map[i].size);
+ if (err < 0)
+ return -ENOMEM;
+ }
+ return 0;
+}
+static int hwreg_map_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hwreg_map_print, inode->i_private);
+}
+
+static const struct file_operations hwreg_map_fops = {
+ .open = hwreg_map_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+/*
+ * HWREG DB8500 formated routines
+ */
+
+static int hwreg_print(struct seq_file *s, void *d)
+{
+ struct hwreg_cfg *c = (struct hwreg_cfg*) s->private;
+ void *p;
+ uint v;
+
+ if ((c==NULL) || ((p = hwreg_io_ptov(c->addr))==NULL))
+ return -EADDRNOTAVAIL;
+
+ v = (uint) (REG_FMT_32B(c) ? readl(p) : REG_FMT_16B(c) ? readw(p) : readb(p));
+ v = (c->shift>=0) ? v >> c->shift : v << (-c->shift);
+ v = v & c->mask;
+
+ if (REG_FMT_DEC(c))
+ seq_printf(s, "%d\n", v);
+ else if (REG_FMT_32B(c))
+ seq_printf(s, "0x%08X\n", v);
+ else if (REG_FMT_32B(c))
+ seq_printf(s, "0x%04X\n", v);
+ else
+ seq_printf(s, "0x%02X\n", v);
+ return 0;
+}
+
+static int hwreg_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hwreg_print, inode->i_private);
+}
+
+/*
+ * return length of an ASCII numerical value, 0 is string is not a numerical
+ * value. string shall start at value 1st char.
+ * string can be tailed with \0 or space or newline chars only.
+ * value can be decimal or hexadecimal (prefixed 0x or 0X).
+ */
+static int strval_len(char *b)
+{
+ char *s = b;
+ if((*s=='0') && ((*(s+1)=='x') || (*(s+1)=='X'))) {
+ s += 2;
+ for (; *s && (*s!=' ') && (*s!='\n'); s++) {
+ if (!isxdigit(*s))
+ return 0;
+ }
+ } else {
+ if (*s=='-')
+ s++;
+ for (; *s && (*s!=' ') && (*s!='\n'); s++) {
+ if (!isdigit(*s))
+ return 0;
+ }
+ }
+ return (int) (s-b);
+}
+
+/*
+ * parse hwreg input data.
+ * update global hwreg_cfg only if input data syntax is ok.
+ */
+static ssize_t hwreg_common_write(char *b, struct hwreg_cfg *cfg)
+{
+ uint write, val=0, offset=0;
+ struct hwreg_cfg loc = {
+ .addr = 0, /* default: invalid phys addr */
+ .fmt = 0, /* default: 32bit access, hex output */
+ .mask = 0xFFFFFFFF, /* default: no mask */
+ .shift = 0, /* default: no bit shift */
+ };
+
+ /* read or write ? */
+ if(!strncmp(b, "read ", 5)) {
+ write = 0;
+ b += 5;
+ } else if (!strncmp(b, "write ", 6)) {
+ write = 1;
+ b += 6;
+ } else {
+ return -EINVAL;
+ }
+
+ /* OPTIONS -l|-w|-b -s -m -o */
+ while((*b==' ') || (*b=='-')) {
+ if (*(b-1)!=' ') {
+ b++;
+ continue;
+ }
+ if ((!strncmp(b, "-d ", 3)) || (!strncmp(b, "-dec ", 5))) {
+ b += (*(b+2)==' ') ? 3 : 5;
+ loc.fmt |= (1<<0);
+ } else if ((!strncmp(b, "-h ", 3)) || (!strncmp(b, "-hex ", 5))) {
+ b += (*(b+2)==' ') ? 3 : 5;
+ loc.fmt &= ~(1<<0);
+ } else if ((!strncmp(b, "-m ", 3)) || (!strncmp(b, "-mask ", 6))) {
+ b += (*(b+2)==' ') ? 3 : 6;
+ if (strval_len(b)==0)
+ return -EINVAL;
+ loc.mask = simple_strtoul(b, &b, 0);
+ } else if ((!strncmp(b, "-s ", 3)) || (!strncmp(b,"-shift ", 7))) {
+ b += (*(b+2)==' ') ? 3 : 7;
+ if (strval_len(b)==0)
+ return -EINVAL;
+ loc.shift = simple_strtol(b, &b, 0);
+
+ } else if ((!strncmp(b, "-o ", 3)) || (!strncmp(b,"-offset ", 8))) {
+ b += (*(b+2)==' ') ? 3 : 8;
+ if (strval_len(b)==0)
+ return -EINVAL;
+ offset = simple_strtol(b, &b, 0);
+ } else if (!strncmp(b, "-l ", 3)) {
+ b += 3;
+ loc.fmt = (loc.fmt & ~(3<<1)) | (0<<1);
+ } else if (!strncmp(b, "-w ", 3)) {
+ b += 3;
+ loc.fmt = (loc.fmt & ~(3<<1)) | (1<<1);
+ } else if (!strncmp(b, "-b ", 3)) {
+ b += 3;
+ loc.fmt = (loc.fmt & ~(3<<1)) | (2<<1);
+ } else {
+ return -EINVAL;
+ }
+ }
+ /* get arg ADDRESS */
+ if (strval_len(b)==0)
+ return -EINVAL;
+ loc.addr = simple_strtoul(b, &b, 0);
+ loc.addr += offset;
+ if (hwreg_io_ptov(loc.addr) == NULL)
+ return -EINVAL;
+
+ if (write) {
+ while(*b==' ') b++; /* skip spaces up to arg VALUE */
+ if (strval_len(b)==0)
+ return -EINVAL;
+ val = simple_strtoul(b, &b, 0);
+ }
+
+ /* args are ok, update target cfg (mainly for read) */
+ *cfg = loc;
+
+#ifdef DEBUG
+ printk(KERN_INFO "HWREG request: %s %d-bit reg, %s, addr=0x%08X, "
+ "mask=0x%X, shift=%d value=0x%X\n",
+ (write)?"write":"read",
+ REG_FMT_32B(cfg)?32:REG_FMT_16B(cfg)?16:8,
+ REG_FMT_DEC(cfg)?"decimal":"hexa",
+ cfg->addr, cfg->mask, cfg->shift, val);
+#endif
+
+ if (write) {
+ void *p = hwreg_io_ptov(cfg->addr);
+ uint d = (uint) (REG_FMT_32B(cfg)) ? readl(p) :
+ (REG_FMT_16B(cfg)) ? readw(p) : readb(p);
+
+ if (cfg->shift>=0) {
+ d &= ~(cfg->mask << (cfg->shift));
+ val = (val & cfg->mask) << (cfg->shift);
+ } else {
+ d &= ~(cfg->mask >> (-cfg->shift));
+ val = (val & cfg->mask) >> (-cfg->shift);
+ }
+ val = val | d;
+
+ /* read reg, reset mask field and update value bit-field */
+ if (REG_FMT_32B(cfg))
+ writel(val, p);
+ else if (REG_FMT_16B(cfg))
+ writew(val, p);
+ else
+ writeb(val, p);
+ }
+ return 0;
+}
+
+static ssize_t hwreg_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[128];
+ int buf_size, ret;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ /* get args and process */
+ ret = hwreg_common_write(buf, &hwreg_cfg);
+ return (ret) ? ret : buf_size;
+}
+
+static const struct file_operations hwreg_fops = {
+ .open = hwreg_open,
+ .write = hwreg_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+/*
+ * hwreg module init/cleanup
+ */
+static int __init hwreg_initialize(void)
+{
+ static struct dentry *file;
+ hwreg_io_init();
+
+ hwreg_debugfs_dir = debugfs_create_dir("mem", NULL);
+ if (!hwreg_debugfs_dir)
+ goto debugfs_err;
+
+ file = debugfs_create_file("reg-addr",
+ (S_IRUGO | S_IWUGO), hwreg_debugfs_dir,
+ NULL, &hwreg_address_fops);
+ if (!file)
+ goto debugfs_err;
+ file = debugfs_create_file("reg-val",
+ (S_IRUGO | S_IWUGO), hwreg_debugfs_dir,
+ NULL, &hwreg_value_fops);
+ if (!file)
+ goto debugfs_err;
+ file = debugfs_create_file("reg-map",
+ (S_IRUGO),
+ hwreg_debugfs_dir, NULL, &hwreg_map_fops);
+ if (!file)
+ goto debugfs_err;
+ file = debugfs_create_file("hwreg",
+ (S_IRUGO),
+ hwreg_debugfs_dir, &hwreg_cfg, &hwreg_fops);
+ if (!file)
+ goto debugfs_err;
+ return 0;
+
+debugfs_err:
+ if (hwreg_debugfs_dir)
+ debugfs_remove_recursive(hwreg_debugfs_dir);
+ printk(KERN_ERR "hwreg: failed to register debugfs entries.\n");
+ return -1;
+}
+
+static void __exit hwreg_finalize(void)
+{
+ debugfs_remove_recursive(hwreg_debugfs_dir);
+ hwreg_io_exit();
+}
+
+module_init(hwreg_initialize);
+module_exit(hwreg_finalize);
+
+MODULE_AUTHOR("ST-Ericsson");
+MODULE_DESCRIPTION("DB8500 HW registers access through debugfs");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 5664696f2d3..084568662c5 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -500,6 +500,7 @@ config USB_SWITCH_FSA9480
stereo and mono audio, video, microphone and UART data to use
a common connector port.
+source "drivers/misc/Kconfig.stm"
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Kconfig.stm b/drivers/misc/Kconfig.stm
new file mode 100644
index 00000000000..ef2b94683a3
--- /dev/null
+++ b/drivers/misc/Kconfig.stm
@@ -0,0 +1,114 @@
+menuconfig STM_TRACE
+ bool "STM MIPI Trace driver"
+ depends on ARCH_U8500
+ help
+ Simple System Trace Module driver. It allows to use and configure the
+ STM, either from kernel space, or from user space.
+
+if STM_TRACE
+
+config STM_NUMBER_OF_CHANNEL
+ int
+ default 512 if ARCH_U8500
+ default 256
+ help
+ Number Max of channels always a multiple of 256
+
+config STM_PRINTK
+ bool "printk support"
+ depends on STM_TRACE
+ help
+ Duplicate printk output on STM printk channel & activate stm_printk
+
+config STM_PRINTK_CHANNEL
+ int "printk channel"
+ range 0 255
+ depends on STM_PRINTK
+ default 255
+ help
+ STM printk channel number
+
+config STM_FTRACE
+ bool "functions tracing"
+ depends on FTRACE
+ default y
+ help
+ Output function tracing on STM dedicated channel
+
+config STM_FTRACE_CHANNEL
+ int "ftrace channel"
+ range 0 255
+ depends on STM_FTRACE
+ default 254
+ help
+ STM ftrace channel number
+
+config STM_CTX_SWITCH
+ bool "Context switch tracing"
+ depends on CONTEXT_SWITCH_TRACER
+ default y
+ help
+ Output scheduler context switch on STM dedicated channel
+
+config STM_CTX_SWITCH_CHANNEL
+ int "Context switch channel"
+ range 0 255
+ depends on STM_CTX_SWITCH
+ default 253
+ help
+ STM Context switch channel number
+
+config STM_WAKEUP
+ bool "Scheduler wakeup tracing"
+ depends on CONTEXT_SWITCH_TRACER
+ default y
+ help
+ Output scheduler wakeup on STM dedicated channel
+
+config STM_WAKEUP_CHANNEL
+ int "Wakeup channel"
+ range 0 255
+ depends on STM_WAKEUP
+ default 252
+ help
+ STM scheduler wakeup channel number
+
+config STM_STACK_TRACE
+ bool "Stack tracing"
+ depends on STACKTRACE
+ default y
+ help
+ Output stack tracing on STM dedicated channel
+
+config STM_STACK_TRACE_CHANNEL
+ int "Stack trace channel"
+ range 0 255
+ depends on STM_STACK_TRACE
+ default 251
+ help
+ STM stack trace channel number
+
+config STM_TRACE_PRINTK
+ bool "trace printk & binary printk support"
+ depends on TRACING
+ default y
+ help
+ Duplicate trace printk output on STM printk channel
+
+config STM_TRACE_PRINTK_CHANNEL
+ int "trace_printk channel"
+ range 0 255
+ depends on TRACING
+ default 250
+ help
+ STM trace_printk channel number
+
+config STM_TRACE_BPRINTK_CHANNEL
+ int "trace_bprintk channel"
+ range 0 255
+ depends on TRACING
+ default 249
+ help
+ STM trace binary printk channel number
+
+endif
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index b26495a0255..0ee9a1ef98b 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -46,5 +46,6 @@ obj-y += ti-st/
obj-$(CONFIG_AB8500_PWM) += ab8500-pwm.o
obj-y += lis3lv02d/
obj-y += carma/
+obj-$(CONFIG_STM_TRACE) += stm.o
obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/
diff --git a/drivers/misc/stm.c b/drivers/misc/stm.c
new file mode 100644
index 00000000000..a05b2d14573
--- /dev/null
+++ b/drivers/misc/stm.c
@@ -0,0 +1,797 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson.
+ * Philippe Langlais <philippe.Langlais@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/cdev.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <trace/stm.h>
+
+/* STM Registers */
+#define STM_CR (stm.virtbase)
+#define STM_MMC (stm.virtbase + 0x008)
+#define STM_TER (stm.virtbase + 0x010)
+#define STMPERIPHID0 (stm.virtbase + 0xFC0)
+#define STMPERIPHID1 (stm.virtbase + 0xFC8)
+#define STMPERIPHID2 (stm.virtbase + 0xFD0)
+#define STMPERIPHID3 (stm.virtbase + 0xFD8)
+#define STMPCELLID0 (stm.virtbase + 0xFE0)
+#define STMPCELLID1 (stm.virtbase + 0xFE8)
+#define STMPCELLID2 (stm.virtbase + 0xFF0)
+#define STMPCELLID3 (stm.virtbase + 0xFF8)
+
+#define STM_CLOCK_SHIFT 6
+#define STM_CLOCK_MASK 0x1C0
+
+/* Hardware mode for all sources */
+#define STM_MMC_DEFAULT 0xFFFFFFFF
+
+/* Max number of channels (multiple of 256) */
+#define STM_NUMBER_OF_CHANNEL CONFIG_STM_NUMBER_OF_CHANNEL
+
+/* # dynamically allocated channel with stm_trace_buffer */
+#define NB_KERNEL_DYNAMIC_CHANNEL 128
+
+static struct stm_device {
+ const struct stm_platform_data *pdata;
+ void __iomem *virtbase;
+ /* Used to register the allocated channels */
+ DECLARE_BITMAP(ch_bitmap, STM_NUMBER_OF_CHANNEL);
+} stm;
+
+volatile struct stm_channel __iomem *stm_channels;
+
+static struct cdev cdev;
+static struct class *stm_class;
+static int stm_major;
+
+static DEFINE_SPINLOCK(lock);
+
+/* Middle value for clock divisor */
+static enum clock_div stm_clockdiv = STM_CLOCK_DIV8;
+
+/* Default value for STM output connection */
+static enum stm_connection_type stm_connection = STM_DEFAULT_CONNECTION;
+
+#define STM_BUFSIZE 256
+struct channel_data {
+ DECLARE_BITMAP(bitmap, STM_NUMBER_OF_CHANNEL);
+ int numero;
+ spinlock_t lock;
+ u8 data_buffer[STM_BUFSIZE];
+};
+
+static u64 stm_printk_buf[1024/sizeof(u64)];
+static arch_spinlock_t stm_buf_lock =
+ (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED;
+
+int stm_alloc_channel(int offset)
+{
+ int channel;
+
+ /* Look for a free channel from offset */
+ do {
+ channel = find_next_zero_bit(stm.ch_bitmap,
+ STM_NUMBER_OF_CHANNEL, offset);
+ } while ((channel < STM_NUMBER_OF_CHANNEL)
+ && test_and_set_bit(channel, stm.ch_bitmap));
+ return channel;
+}
+EXPORT_SYMBOL(stm_alloc_channel);
+
+void stm_free_channel(int channel)
+{
+ clear_bit(channel, stm.ch_bitmap);
+}
+EXPORT_SYMBOL(stm_free_channel);
+
+static int stm_get_channel(struct channel_data *ch_data, int __user *arg)
+{
+ int channel, err;
+
+ channel = stm_alloc_channel(0);
+ if (channel < STM_NUMBER_OF_CHANNEL) {
+ /* One free found ! */
+ err = put_user(channel, arg);
+ if (err)
+ stm_free_channel(channel);
+ else
+ /* Register it in the context of the file */
+ set_bit(channel, ch_data->bitmap);
+ } else
+ err = -ENOMEM;
+ return err;
+}
+
+static int stm_release_channel(struct channel_data *ch_data, int channel)
+{
+ if ((channel < 0) || (channel >= STM_NUMBER_OF_CHANNEL))
+ return -EINVAL;
+ stm_free_channel(channel);
+ clear_bit(channel, ch_data->bitmap);
+ return 0;
+}
+
+/*
+ * Trace a buffer on a given channel
+ * with auto time stamping on last byte(s) only
+ */
+int stm_trace_buffer_onchannel(int channel,
+ const void *data, size_t length)
+{
+ int i, mod64;
+ volatile struct stm_channel __iomem *pch;
+
+ if (channel >= STM_NUMBER_OF_CHANNEL || !stm_channels)
+ return 0;
+
+ pch = &stm_channels[channel];
+
+ /* Align data pointer to u64 & time stamp last byte(s) */
+ mod64 = (int)data & 7;
+ i = length - 8 + mod64;
+ switch (mod64) {
+ case 0:
+ if (i)
+ pch->no_stamp64 = *(u64 *)data;
+ else {
+ pch->stamp64 = *(u64 *)data;
+ return length;
+ }
+ data += 8;
+ break;
+ case 1:
+ pch->no_stamp8 = *(u8 *)data;
+ pch->no_stamp16 = *(u16 *)(data+1);
+ if (i)
+ pch->no_stamp32 = *(u32 *)(data+3);
+ else {
+ pch->stamp32 = *(u32 *)(data+3);
+ return length;
+ }
+ data += 7;
+ break;
+ case 2:
+ pch->no_stamp16 = *(u16 *)data;
+ if (i)
+ pch->no_stamp32 = *(u32 *)(data+2);
+ else {
+ pch->stamp32 = *(u32 *)(data+2);
+ return length;
+ }
+ data += 6;
+ break;
+ case 3:
+ pch->no_stamp8 = *(u8 *)data;
+ if (i)
+ pch->no_stamp32 = *(u32 *)(data+1);
+ else {
+ pch->stamp32 = *(u32 *)(data+1);
+ return length;
+ }
+ data += 5;
+ break;
+ case 4:
+ if (i)
+ pch->no_stamp32 = *(u32 *)data;
+ else {
+ pch->stamp32 = *(u32 *)data;
+ return length;
+ }
+ data += 4;
+ break;
+ case 5:
+ pch->no_stamp8 = *(u8 *)data;
+ if (i)
+ pch->no_stamp16 = *(u16 *)(data+1);
+ else {
+ pch->stamp16 = *(u16 *)(data+1);
+ return length;
+ }
+ data += 3;
+ break;
+ case 6:
+ if (i)
+ pch->no_stamp16 = *(u16 *)data;
+ else {
+ pch->stamp16 = *(u16 *)data;
+ return length;
+ }
+ data += 2;
+ break;
+ case 7:
+ if (i)
+ pch->no_stamp8 = *(u8 *)data;
+ else {
+ pch->stamp8 = *(u8 *)data;
+ return length;
+ }
+ data++;
+ break;
+ }
+ for (;;) {
+ if (i > 8) {
+ pch->no_stamp64 = *(u64 *)data;
+ data += 8;
+ i -= 8;
+ } else if (i == 8) {
+ pch->stamp64 = *(u64 *)data;
+ break;
+ } else if (i > 4) {
+ pch->no_stamp32 = *(u32 *)data;
+ data += 4;
+ i -= 4;
+ } else if (i == 4) {
+ pch->stamp32 = *(u32 *)data;
+ break;
+ } else if (i > 2) {
+ pch->no_stamp16 = *(u16 *)data;
+ data += 2;
+ i -= 2;
+ } else if (i == 2) {
+ pch->stamp16 = *(u16 *)data;
+ break;
+ } else {
+ pch->stamp8 = *(u8 *)data;
+ break;
+ }
+ }
+ return length;
+}
+EXPORT_SYMBOL(stm_trace_buffer_onchannel);
+
+static int stm_open(struct inode *inode, struct file *file)
+{
+ struct channel_data *channel_data;
+
+ channel_data = kzalloc(sizeof(struct channel_data), GFP_KERNEL);
+ if (channel_data == NULL)
+ return -ENOMEM;
+
+ spin_lock_init(&channel_data->lock);
+ channel_data->numero = -1; /* Channel not yet allocated */
+ file->private_data = channel_data;
+
+ return 0;
+}
+
+static int stm_release(struct inode *inode, struct file *file)
+{
+ struct channel_data *channel;
+
+ channel = (struct channel_data *)file->private_data;
+
+ /* Free allocated channel if necessary */
+ if (channel->numero != -1)
+ stm_free_channel(channel->numero);
+
+ bitmap_andnot(stm.ch_bitmap, stm.ch_bitmap,
+ channel->bitmap, STM_NUMBER_OF_CHANNEL);
+
+ kfree(channel);
+ return 0;
+}
+
+static ssize_t stm_write(struct file *file, const char __user *buf,
+ size_t size, loff_t *off)
+{
+ struct channel_data *channel = file->private_data;
+
+ /* Alloc channel at first write */
+ if (channel->numero == -1) {
+ channel->numero = stm_alloc_channel(0);
+ if (channel->numero > STM_NUMBER_OF_CHANNEL)
+ return -ENOMEM;
+ }
+
+ if (size > STM_BUFSIZE)
+ size = STM_BUFSIZE;
+
+ spin_lock(&channel->lock);
+
+ if (copy_from_user
+ (channel->data_buffer, (void __user *) buf, size)) {
+ spin_unlock(&channel->lock);
+ return -EFAULT;
+ }
+ size = stm_trace_buffer_onchannel(channel->numero,
+ channel->data_buffer, size);
+
+ spin_unlock(&channel->lock);
+
+ return size;
+}
+
+static int stm_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ /*
+ * Don't allow a mapping that covers more than the STM channels
+ */
+ if ((vma->vm_end - vma->vm_start) >
+ STM_NUMBER_OF_CHANNEL*sizeof(struct stm_channel))
+ return -EINVAL;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ if (io_remap_pfn_range(vma, vma->vm_start,
+ stm.pdata->channels_phys_base>>PAGE_SHIFT,
+ STM_NUMBER_OF_CHANNEL*sizeof(struct stm_channel),
+ vma->vm_page_prot))
+ return -EAGAIN;
+
+ return 0;
+}
+
+/* Enable the trace for given sources (bitfield) */
+static void stm_enable_src(unsigned int v)
+{
+ unsigned int cr_val;
+ spin_lock(&lock);
+ cr_val = readl(STM_CR);
+ cr_val &= ~STM_CLOCK_MASK;
+ writel(cr_val|(stm_clockdiv<<STM_CLOCK_SHIFT), STM_CR);
+ writel(v, STM_TER);
+ spin_unlock(&lock);
+}
+
+/* Disable all sources */
+static void stm_disable_src(void)
+{
+ writel(0x0, STM_CR); /* stop clock */
+ writel(0x0, STM_TER); /* Disable cores */
+}
+
+/* Set clock speed */
+static int stm_set_ckdiv(enum clock_div v)
+{
+ unsigned int val;
+
+ spin_lock(&lock);
+ val = readl(STM_CR);
+ val &= ~STM_CLOCK_MASK;
+ writel(val | ((v << STM_CLOCK_SHIFT) & STM_CLOCK_MASK), STM_CR);
+ spin_unlock(&lock);
+ stm_clockdiv = v;
+
+ return 0;
+}
+
+/* Return the control register */
+static inline unsigned int stm_get_cr(void)
+{
+ return readl(STM_CR);
+}
+
+/*
+ * Set Trace MODE lossless/lossy (Software/Hardware)
+ * each bit represent the corresponding mode of this source
+ */
+static inline void stm_set_modes(unsigned int modes)
+{
+ writel(modes, STM_MMC);
+}
+
+/* Get Trace MODE lossless/lossy (Software/Hardware)
+ * each bit represent the corresponding mode of this source */
+static inline unsigned int stm_get_modes(void)
+{
+ return readl(STM_MMC);
+}
+
+/* Count # of free channels */
+static int stm_nb_free_channels(void)
+{
+ int nb_channels, offset;
+
+ nb_channels = 0;
+ offset = 0;
+ for (;;) {
+ offset = find_next_zero_bit(stm.ch_bitmap,
+ STM_NUMBER_OF_CHANNEL, offset);
+ if (offset == STM_NUMBER_OF_CHANNEL)
+ break;
+ offset++;
+ nb_channels++;
+ }
+ return nb_channels;
+}
+
+static long stm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int err = 0;
+ struct channel_data *channel = file->private_data;
+
+ switch (cmd) {
+
+ case STM_CONNECTION:
+ if (stm.pdata->stm_connection)
+ stm.pdata->stm_connection(arg);
+ stm_connection = arg;
+ break;
+
+ case STM_DISABLE:
+ stm_disable_src();
+ break;
+
+ case STM_GET_NB_MAX_CHANNELS:
+ err = put_user(STM_NUMBER_OF_CHANNEL, (unsigned int *)arg);
+ break;
+
+ case STM_GET_NB_FREE_CHANNELS:
+ err = put_user(stm_nb_free_channels(), (unsigned int *)arg);
+ break;
+
+ case STM_GET_CHANNEL_NO:
+ err = put_user(channel->numero, (unsigned int *)arg);
+ break;
+
+ case STM_SET_CLOCK_DIV:
+ err = stm_set_ckdiv((enum clock_div) arg);
+ break;
+
+ case STM_SET_MODE:
+ stm_set_modes(arg);
+ break;
+
+ case STM_GET_MODE:
+ err = put_user(stm_get_modes(), (unsigned int *)arg);
+ break;
+
+ case STM_GET_CTRL_REG:
+ err = put_user(stm_get_cr(), (unsigned int *)arg);
+ break;
+
+ case STM_ENABLE_SRC:
+ stm_enable_src(arg);
+ break;
+
+ case STM_GET_FREE_CHANNEL:
+ err = stm_get_channel(channel, (int *)arg);
+ break;
+
+ case STM_RELEASE_CHANNEL:
+ err = stm_release_channel(channel, arg);
+ break;
+
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+/*
+ * Trace a buffer on a dynamically allocated channel
+ * with auto time stamping on the first byte(s) only
+ * Dynamic channel number >=
+ * STM_NUMBER_OF_CHANNEL - NB_KERNEL_DYNAMIC_CHANNEL
+ */
+int stm_trace_buffer(const void *data, size_t length)
+{
+ int channel;
+
+ channel = stm_alloc_channel(STM_NUMBER_OF_CHANNEL
+ - NB_KERNEL_DYNAMIC_CHANNEL);
+ if (channel < STM_NUMBER_OF_CHANNEL) {
+ length = stm_trace_buffer_onchannel(channel, data, length);
+ stm_free_channel(channel);
+ return length;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(stm_trace_buffer);
+
+static const struct file_operations stm_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = stm_ioctl,
+ .open = stm_open,
+ .llseek = no_llseek,
+ .write = stm_write,
+ .release = stm_release,
+ .mmap = stm_mmap,
+};
+
+/*
+ * Init and deinit driver
+ */
+
+static int __devinit stm_probe(struct platform_device *pdev)
+{
+ int retval = 0;
+
+ if (!pdev || !pdev->dev.platform_data) {
+ pr_alert("No device/platform_data found on STM driver\n");
+ return -ENODEV;
+ }
+
+ stm.pdata = pdev->dev.platform_data;
+
+ cdev_init(&cdev, &stm_fops);
+ cdev.owner = THIS_MODULE;
+
+ stm_channels =
+ ioremap_nocache(stm.pdata->channels_phys_base,
+ STM_NUMBER_OF_CHANNEL*sizeof(*stm_channels));
+ if (stm_channels == NULL) {
+ dev_err(&pdev->dev, "could not remap STM Msg register\n");
+ return -ENODEV;
+ }
+
+ stm.virtbase = ioremap_nocache(stm.pdata->regs_phys_base, SZ_4K);
+ if (stm.virtbase == NULL) {
+ retval = -EIO;
+ dev_err(&pdev->dev, "could not remap STM Register\n");
+ goto err_channels;
+ }
+
+ retval = cdev_add(&cdev, MKDEV(stm_major, 0), 1);
+ if (retval) {
+ dev_err(&pdev->dev, "chardev registration failed\n");
+ goto err_channels;
+ }
+
+ if (IS_ERR(device_create(stm_class, &pdev->dev,
+ MKDEV(stm_major, 0), NULL, STM_DEV_NAME)))
+ dev_err(&pdev->dev, "can't create device\n");
+
+ /* Check chip IDs if necessary */
+ if (stm.pdata->id_mask) {
+ u32 periph_id, cell_id;
+
+ periph_id = (readb(STMPERIPHID3)<<24) +
+ (readb(STMPERIPHID2)<<16) +
+ (readb(STMPERIPHID1)<<8) +
+ readb(STMPERIPHID0);
+ cell_id = (readb(STMPCELLID3)<<24) +
+ (readb(STMPCELLID2)<<16) +
+ (readb(STMPCELLID1)<<8) +
+ readb(STMPCELLID0);
+ /* Only warns if it isn't a ST-Ericsson supported one */
+ if ((periph_id & stm.pdata->id_mask) != 0x00080dec ||
+ cell_id != 0xb105f00d) {
+ dev_warn(&pdev->dev, "STM-Trace IC not compatible\n");
+ dev_warn(&pdev->dev, "periph_id=%x\n", periph_id);
+ dev_warn(&pdev->dev, "pcell_id=%x\n", cell_id);
+ }
+ }
+
+ /* Reserve channels if necessary */
+ if (stm.pdata->channels_reserved_sz) {
+ int i;
+
+ for (i = 0; i < stm.pdata->channels_reserved_sz; i++) {
+ set_bit(stm.pdata->channels_reserved[i],
+ stm.ch_bitmap);
+ }
+ }
+ /* Reserve kernel trace channels on demand */
+#ifdef CONFIG_STM_PRINTK
+ set_bit(CONFIG_STM_PRINTK_CHANNEL, stm.ch_bitmap);
+#endif
+#ifdef CONFIG_STM_FTRACE
+ set_bit(CONFIG_STM_FTRACE_CHANNEL, stm.ch_bitmap);
+#endif
+#ifdef CONFIG_STM_CTX_SWITCH
+ set_bit(CONFIG_STM_CTX_SWITCH_CHANNEL, stm.ch_bitmap);
+#endif
+#ifdef CONFIG_STM_WAKEUP
+ set_bit(CONFIG_STM_WAKEUP_CHANNEL, stm.ch_bitmap);
+#endif
+#ifdef CONFIG_STM_STACK_TRACE
+ set_bit(CONFIG_STM_STACK_TRACE_CHANNEL, stm.ch_bitmap);
+#endif
+#ifdef CONFIG_STM_TRACE_PRINTK
+ set_bit(CONFIG_STM_TRACE_PRINTK_CHANNEL, stm.ch_bitmap);
+ set_bit(CONFIG_STM_TRACE_BPRINTK_CHANNEL, stm.ch_bitmap);
+#endif
+
+ if (stm.pdata->stm_connection) {
+ retval = stm.pdata->stm_connection(stm_connection);
+ if (retval) {
+ dev_err(&pdev->dev, "failed to connect STM output\n");
+ goto err_channels;
+ }
+ }
+
+ /* Enable STM Masters given in pdata */
+ if (stm.pdata->masters_enabled)
+ stm_enable_src(stm.pdata->masters_enabled);
+
+ stm_set_modes(STM_MMC_DEFAULT); /* Set all sources in HW mode */
+
+ dev_info(&pdev->dev, "STM-Trace driver probed successfully\n");
+ stm_printk("STM-Trace driver initialized\n");
+ return 0;
+
+err_channels:
+ iounmap(stm_channels);
+ return retval;
+}
+
+static int __devexit stm_remove(struct platform_device *pdev)
+{
+ device_destroy(stm_class, MKDEV(stm_major, 0));
+ cdev_del(&cdev);
+
+ if (stm.pdata->stm_connection)
+ (void) stm.pdata->stm_connection(STM_DISCONNECT);
+
+ stm_disable_src();
+ iounmap(stm.virtbase);
+ iounmap(stm_channels);
+
+ return 0;
+}
+
+int stm_printk(const char *fmt, ...)
+{
+ int ret;
+ size_t size;
+ va_list args;
+
+ va_start(args, fmt);
+ arch_spin_lock(&stm_buf_lock);
+ size = vscnprintf((char *)stm_printk_buf,
+ sizeof(stm_printk_buf), fmt, args);
+ ret = stm_trace_buffer(stm_printk_buf, size);
+ arch_spin_unlock(&stm_buf_lock);
+ va_end(args);
+ return ret;
+}
+EXPORT_SYMBOL(stm_printk);
+
+/*
+ * Debugfs interface
+ */
+
+static int stm_connection_show(void *data, u64 *val)
+{
+ *val = stm_connection;
+ return 0;
+}
+
+static int stm_connection_set(void *data, u64 val)
+{
+ if (stm.pdata->stm_connection) {
+ stm_connection = val;
+ stm.pdata->stm_connection(val);
+ }
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(stm_connection_fops, stm_connection_show,
+ stm_connection_set, "%llu\n");
+
+static int stm_clockdiv_show(void *data, u64 *val)
+{
+ *val = stm_clockdiv;
+ return 0;
+}
+
+static int stm_clockdiv_set(void *data, u64 val)
+{
+ stm_set_ckdiv(val);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(stm_clockdiv_fops, stm_clockdiv_show,
+ stm_clockdiv_set, "%llu\n");
+
+static int stm_masters_enable_show(void *data, u64 *val)
+{
+ *val = readl(STM_TER);
+ return 0;
+}
+
+static int stm_masters_enable_set(void *data, u64 val)
+{
+ stm_enable_src(val);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(stm_masters_enable_fops, stm_masters_enable_show,
+ stm_masters_enable_set, "%08llx\n");
+
+static int stm_masters_modes_show(void *data, u64 *val)
+{
+ *val = stm_get_modes();
+ return 0;
+}
+
+static int stm_masters_modes_set(void *data, u64 val)
+{
+ stm_set_modes(val);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(stm_masters_modes_fops, stm_masters_modes_show,
+ stm_masters_modes_set, "%08llx\n");
+
+/* Count # of free channels */
+static int stm_free_channels_show(void *data, u64 *val)
+{
+ *val = stm_nb_free_channels();
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(stm_free_channels_fops, stm_free_channels_show,
+ NULL, "%lld\n");
+
+static __init int stm_init_debugfs(void)
+{
+ struct dentry *d_stm;
+
+ d_stm = debugfs_create_dir(STM_DEV_NAME, NULL);
+ if (!d_stm)
+ return -ENOMEM;
+
+ (void) debugfs_create_file("connection", S_IRUGO | S_IWUGO, d_stm,
+ NULL, &stm_connection_fops);
+ (void) debugfs_create_file("clockdiv", S_IRUGO | S_IWUGO, d_stm,
+ NULL, &stm_clockdiv_fops);
+ (void) debugfs_create_file("masters_enable", S_IRUGO | S_IWUGO, d_stm,
+ NULL, &stm_masters_enable_fops);
+ (void) debugfs_create_file("masters_modes", S_IRUGO | S_IWUGO, d_stm,
+ NULL, &stm_masters_modes_fops);
+ (void) debugfs_create_file("free_channels", S_IRUGO, d_stm,
+ NULL, &stm_free_channels_fops);
+ return 0;
+}
+fs_initcall(stm_init_debugfs);
+
+static struct platform_driver stm_driver = {
+ .probe = stm_probe,
+ .remove = __devexit_p(stm_remove),
+ .driver = {
+ .name = STM_DEV_NAME,
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init stm_init(void)
+{
+ int retval;
+ dev_t dev;
+
+ stm_class = class_create(THIS_MODULE, STM_DEV_NAME);
+ if (IS_ERR(stm_class)) {
+ pr_err("stm: can't register stm class\n");
+ return PTR_ERR(stm_class);
+ }
+
+ retval = alloc_chrdev_region(&dev, 0, 1, STM_DEV_NAME);
+ if (retval) {
+ pr_err("stm: can't register character device\n");
+ class_destroy(stm_class);
+ return retval;
+ }
+ stm_major = MAJOR(dev);
+ return platform_driver_register(&stm_driver);
+}
+
+static void __exit stm_exit(void)
+{
+ platform_driver_unregister(&stm_driver);
+ unregister_chrdev_region(MKDEV(stm_major, 0), 1);
+ class_destroy(stm_class);
+}
+
+arch_initcall(stm_init); /* STM init ASAP need to wait GPIO init */
+module_exit(stm_exit);
+
+MODULE_AUTHOR("Paul Ghaleb - ST Microelectronics");
+MODULE_AUTHOR("Pierre Peiffer - ST-Ericsson");
+MODULE_AUTHOR("Philippe Langlais - ST-Ericsson");
+MODULE_DESCRIPTION("System Trace Module driver");
+MODULE_ALIAS("stm");
+MODULE_ALIAS("stm-trace");
+MODULE_LICENSE("GPL v2");
diff --git a/include/Kbuild b/include/Kbuild
index 8d226bfa269..506f6d7dba7 100644
--- a/include/Kbuild
+++ b/include/Kbuild
@@ -10,3 +10,4 @@ header-y += video/
header-y += drm/
header-y += xen/
header-y += scsi/
+header-y += trace/
diff --git a/include/linux/boottime.h b/include/linux/boottime.h
new file mode 100644
index 00000000000..9836c5b3175
--- /dev/null
+++ b/include/linux/boottime.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2009-2010
+ *
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * boottime is a tool for collecting start-up timing
+ * information and can together with boot loader support
+ * display a total system start-up time.
+ *
+ */
+
+#ifndef LINUX_BOOTTIME_H
+#define LINUX_BOOTTIME_H
+
+#ifdef CONFIG_BOOTTIME
+#include <linux/kernel.h>
+
+/**
+ * struct boottime_timer - Callbacks for generic timer.
+ * @init: Function to call at boottime initialization
+ * @get_time: Returns the number of us since start-up
+ * Preferable this is based upon a free running timer.
+ * This is the only required entry.
+ * @finalize: Called before init is executed and boottime is done.
+ */
+struct boottime_timer {
+ int (*init)(void);
+ unsigned long (*get_time)(void);
+ void (*finalize)(void);
+};
+
+/**
+ * boottime_mark_wtime()
+ * Add a sample point with a given time. Useful for adding data collected
+ * by for example a boot loader.
+ * @name: The name of the sample point
+ * @time: The time in us when this point was reached
+ */
+void __init boottime_mark_wtime(char *name, unsigned long time);
+
+/**
+ * boottime_mark()
+ * Add a sample point with the current time.
+ * @name: The name of this sample point
+ */
+void __init boottime_mark(char *name);
+
+/**
+ * boottime_mark_symbolic()
+ * Add a sample point where the name is a symbolic function
+ * and %pF is needed to get the correct function name.
+ * @name: function name.
+ */
+void __init boottime_mark_symbolic(void *name);
+
+/**
+ * boottime_activate()
+ * Activates boottime and register callbacks.
+ * @bt: struct with callbacks.
+ */
+void __ref boottime_activate(struct boottime_timer *bt);
+
+/**
+ * boottime_deactivate()
+ * This function is called when the kernel boot is done.
+ * (before "free init memory" is called)
+ */
+void __init boottime_deactivate(void);
+
+/**
+ * boottime_system_up()
+ * A function is called when the basics of the kernel
+ * is up and running.
+ */
+void __init boottime_system_up(void);
+
+#else
+
+#define boottime_mark_wtime(name, time)
+#define boottime_mark(name)
+#define boottime_mark_symbolic(name)
+#define boottime_activate(bt)
+#define boottime_deactivate()
+#define boottime_system_up()
+#endif
+
+#endif /* LINUX_BOOTTIME_H */
diff --git a/include/trace/Kbuild b/include/trace/Kbuild
new file mode 100644
index 00000000000..7e8b704d610
--- /dev/null
+++ b/include/trace/Kbuild
@@ -0,0 +1 @@
+header-y += stm.h
diff --git a/include/trace/stm.h b/include/trace/stm.h
new file mode 100644
index 00000000000..72e9136c25e
--- /dev/null
+++ b/include/trace/stm.h
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson STM Trace driver
+ *
+ * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson.
+ * Philippe Langlais <philippe.langlais@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#ifndef STM_H
+#define STM_H
+
+#define STM_DEV_NAME "stm"
+
+/* One single channel mapping */
+struct stm_channel {
+ union {
+ __u8 no_stamp8;
+ __u16 no_stamp16;
+ __u32 no_stamp32;
+ __u64 no_stamp64;
+ };
+ union {
+ __u8 stamp8;
+ __u16 stamp16;
+ __u32 stamp32;
+ __u64 stamp64;
+ };
+};
+
+/* Possible trace modes */
+#define STM_SW_LOSSLESS 0 /* Software mode: lossless data but intrusive */
+#define STM_HW_LOSSY 1 /* Hardware mode: lossy data but less intrusive */
+
+/* Possible clock setting */
+enum clock_div {
+ STM_CLOCK_DIV2 = 0,
+ STM_CLOCK_DIV4,
+ STM_CLOCK_DIV6,
+ STM_CLOCK_DIV8,
+ STM_CLOCK_DIV10,
+ STM_CLOCK_DIV12,
+ STM_CLOCK_DIV14,
+ STM_CLOCK_DIV16,
+};
+
+/* ioctl commands */
+#define STM_CONNECTION _IOW('t', 0, enum stm_connection_type)
+#define STM_DISABLE _IO('t', 1)
+#define STM_GET_NB_MAX_CHANNELS _IOR('t', 2, int)
+#define STM_GET_NB_FREE_CHANNELS _IOR('t', 3, int)
+#define STM_GET_CHANNEL_NO _IOR('t', 4, int)
+#define STM_SET_CLOCK_DIV _IOW('t', 5, enum clock_div)
+#define STM_GET_CTRL_REG _IOR('t', 6, int)
+#define STM_ENABLE_SRC _IOWR('t', 7, int)
+#define STM_GET_FREE_CHANNEL _IOW('t', 8, int)
+#define STM_RELEASE_CHANNEL _IOW('t', 9, int)
+#define STM_SET_MODE _IOWR('t', 10, int)
+#define STM_GET_MODE _IOR('t', 11, int)
+
+enum stm_connection_type {
+ STM_DISCONNECT = 0,
+ STM_DEFAULT_CONNECTION = 1,
+ STM_STE_MODEM_ON_MIPI34_NONE_ON_MIPI60 = 2,
+ STM_STE_APE_ON_MIPI34_NONE_ON_MIPI60 = 3,
+ STM_STE_MODEM_ON_MIPI34_APE_ON_MIPI60 = 4
+};
+
+#ifdef __KERNEL__
+
+struct stm_platform_data {
+ u32 regs_phys_base;
+ u32 channels_phys_base;
+ u32 id_mask;
+ u32 masters_enabled;
+ const s16 *channels_reserved;
+ int channels_reserved_sz;
+ int (*stm_connection)(enum stm_connection_type);
+};
+
+/* Channels base address */
+extern volatile struct stm_channel __iomem *stm_channels;
+
+/* Provides stm_trace_XX() and stm_tracet_XX() trace API */
+#define DEFLLTFUN(size) \
+static inline void stm_trace_##size(int channel, __u##size data) \
+{ \
+ stm_channels[channel].no_stamp##size = data; \
+} \
+static inline void stm_tracet_##size(int channel, __u##size data) \
+{ \
+ stm_channels[channel].stamp##size = data; \
+} \
+
+DEFLLTFUN(8);
+DEFLLTFUN(16);
+DEFLLTFUN(32);
+DEFLLTFUN(64);
+
+/*
+ * Trace a buffer on a given channel
+ * with auto time stamping on the last byte(s) only
+ */
+int stm_trace_buffer_onchannel(int channel, const void *data, size_t length);
+/*
+ * Trace a buffer on a dynamically allocated channel
+ * with auto time stamping on the last byte(s) only
+ * Dynamic channel are allocated in the 128 highest channels
+ */
+int stm_trace_buffer(const void *data, size_t length);
+
+/* printk equivalent for STM */
+int stm_printk(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
+
+#if defined(CONFIG_STM_PRINTK)
+#define stm_dup_printk(buf, length) \
+ stm_trace_buffer_onchannel(CONFIG_STM_PRINTK_CHANNEL, buf, length)
+
+#else
+static inline int stm_dup_printk(char *buf, size_t size)
+{
+ return 0;
+}
+#endif
+
+#if defined(CONFIG_STM_TRACE_PRINTK)
+static inline int stm_trace_printk_buf(
+ unsigned long ip, const char *buf, size_t size)
+{
+ stm_trace_32(CONFIG_STM_TRACE_PRINTK_CHANNEL, ip);
+ return stm_trace_buffer_onchannel(CONFIG_STM_TRACE_PRINTK_CHANNEL,
+ buf, size);
+}
+
+static inline int stm_trace_bprintk_buf(
+ unsigned long ip, const char *fmt, const void *buf, size_t size)
+{
+ stm_trace_64(CONFIG_STM_TRACE_BPRINTK_CHANNEL, ((u64)ip<<32)+(u32)fmt);
+ return stm_trace_buffer_onchannel(CONFIG_STM_TRACE_PRINTK_CHANNEL,
+ buf, size);
+}
+#else
+static inline int stm_trace_printk_buf(
+ unsigned long ip, const char *buf, size_t size)
+{
+ return 0;
+}
+
+static inline int stm_trace_bprintk_buf(
+ unsigned long ip, const char *fmt, const void *buf, size_t size)
+{
+ return 0;
+}
+#endif
+
+#if defined(CONFIG_STM_FTRACE)
+static inline void stm_ftrace(unsigned long ip, unsigned long parent_ip)
+{
+ stm_tracet_64(CONFIG_STM_FTRACE_CHANNEL, (((__u64)ip)<<32) + parent_ip);
+}
+#else
+static inline void stm_ftrace(unsigned long ip, unsigned long parent_ip)
+{
+}
+#endif
+
+#if defined(CONFIG_STM_CTX_SWITCH)
+static inline void stm_sched_switch(u32 prev_pid, u8 prev_prio, u8 prev_state,
+ u32 next_pid, u8 next_prio, u8 next_state, u32 next_cpu)
+{
+ stm_trace_64(CONFIG_STM_CTX_SWITCH_CHANNEL,
+ (((__u64)prev_pid)<<32) + next_pid);
+ stm_tracet_64(CONFIG_STM_CTX_SWITCH_CHANNEL, (((__u64)next_cpu)<<32)
+ + (prev_prio<<24) + (prev_state<<16)
+ + (next_prio<<8) + next_state);
+}
+#else
+static inline void stm_sched_switch(u32 prev_pid, u8 prev_prio, u8 prev_state,
+ u32 next_pid, u8 next_prio, u8 next_state, u32 next_cpu)
+{
+}
+#endif
+
+#if defined(CONFIG_STM_WAKEUP)
+static inline void stm_sched_wakeup(u32 prev_pid, u8 prev_prio, u8 prev_state,
+ u32 next_pid, u8 next_prio, u8 next_state, u32 next_cpu)
+{
+ stm_trace_64(CONFIG_STM_WAKEUP_CHANNEL,
+ (((__u64)prev_pid)<<32) + next_pid);
+ stm_tracet_64(CONFIG_STM_WAKEUP_CHANNEL, (((__u64)next_cpu)<<32)
+ + (prev_prio<<24) + (prev_state<<16)
+ + (next_prio<<8) + next_state);
+}
+#else
+static inline void stm_sched_wakeup(u32 prev_pid, u8 prev_prio, u8 prev_state,
+ u32 next_pid, u8 next_prio, u8 next_state, u32 next_cpu)
+{
+}
+#endif
+
+#if defined(CONFIG_STM_STACK_TRACE)
+static inline void stm_stack_trace(unsigned long *callers)
+{
+ while (*(callers + 1) != ULONG_MAX) {
+ stm_trace_32(CONFIG_STM_STACK_TRACE_CHANNEL, *callers++);
+ }
+ /* Time stamp the latest */
+ stm_tracet_32(CONFIG_STM_STACK_TRACE_CHANNEL, *callers);
+}
+#else
+static inline void stm_stack_trace(unsigned long *callers)
+{
+}
+#endif
+
+/* Alloc/Free STM channel */
+int stm_alloc_channel(int offset);
+void stm_free_channel(int channel);
+
+#endif /* __KERNEL__ */
+
+#endif /* STM_H */
diff --git a/init/Kconfig b/init/Kconfig
index 43298f9810f..9162a7607e8 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1285,6 +1285,15 @@ config PROFILING
Say Y here to enable the extended profiling support mechanisms used
by profilers such as OProfile.
+config BOOTTIME
+ bool "Boot time measurments"
+ default n
+ help
+ Adds sysfs entries (boottime/) with start-up timing information.
+ If CONFIG_DEBUG_FS is enabled, detailed information about the
+ boot time, including system load during boot can be extraced.
+ This information can be visualised with help of the bootgraph script.
+
#
# Place an empty function call at each tracepoint site. Can be
# dynamically changed for a probe function.
diff --git a/init/Makefile b/init/Makefile
index 0bf677aa087..6b77be3855f 100644
--- a/init/Makefile
+++ b/init/Makefile
@@ -9,6 +9,7 @@ else
obj-$(CONFIG_BLK_DEV_INITRD) += initramfs.o
endif
obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o
+obj-$(CONFIG_BOOTTIME) += boottime.o
mounts-y := do_mounts.o
mounts-$(CONFIG_BLK_DEV_RAM) += do_mounts_rd.o
diff --git a/init/boottime.c b/init/boottime.c
new file mode 100644
index 00000000000..be73e0ee550
--- /dev/null
+++ b/init/boottime.c
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2009-2010
+ *
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * boottime is a tool for collecting start-up timing
+ * information and can together with boot loader support
+ * display a total system start-up time.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/spinlock.h>
+#include <linux/boottime.h>
+#include <linux/kernel_stat.h>
+#include <linux/kobject.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/slab.h>
+
+/*
+ * BOOTTIME_MAX_NAME_LEN is defined in arch/arm/include/asm/setup.h to 64.
+ * No crisis if they don't match.
+ */
+#ifndef BOOTTIME_MAX_NAME_LEN
+#define BOOTTIME_MAX_NAME_LEN 64
+#endif
+
+/*
+ * We have a few static entries, since it is good to have measure points
+ * before the system is up and running properly
+ */
+#define NUM_STATIC_BOOTTIME_ENTRIES 16
+
+struct boottime_list {
+ struct list_head list;
+ char name[BOOTTIME_MAX_NAME_LEN];
+ /* Time in us since power on, possible including boot loader. */
+ unsigned long time;
+ bool cpu_load;
+ struct cpu_usage_stat cpu_usage[NR_CPUS];
+};
+
+enum boottime_filter_type {
+ BOOTTIME_FILTER_OUT_ZERO,
+ BOOTTIME_FILTER_OUT_LESS_100,
+ BOOTTIME_FILTER_NOTHING,
+};
+
+enum boottime_symbolic_print {
+ BOOTTIME_SYMBOLIC_PRINT,
+ BOOTTIME_NORMAL_PRINT,
+};
+
+enum boottime_cpu_load {
+ BOOTTIME_CPU_LOAD,
+ BOOTTIME_NO_CPU_LOAD,
+};
+
+static LIST_HEAD(boottime_list);
+static __initdata DEFINE_SPINLOCK(boottime_list_lock);
+static __initdata struct boottime_timer boottime_timer;
+static __initdata int num_const_boottime_list;
+static struct boottime_list const_boottime_list[NUM_STATIC_BOOTTIME_ENTRIES];
+static unsigned long time_kernel_done;
+static unsigned long time_bootloader_done;
+static __initdata bool system_up;
+static bool boottime_done;
+
+int __attribute__((weak)) boottime_arch_startup(void)
+{
+ return 0;
+}
+
+int __attribute__((weak)) boottime_bootloader_idle(void)
+{
+ return 0;
+}
+
+static void __init boottime_mark_core(char *name,
+ unsigned long time,
+ enum boottime_symbolic_print symbolic,
+ enum boottime_cpu_load cpu_load)
+{
+ struct boottime_list *b;
+ unsigned long flags = 0;
+ int i;
+
+ if (system_up) {
+ b = kmalloc(sizeof(struct boottime_list), GFP_KERNEL);
+ if (!b) {
+ printk(KERN_ERR
+ "boottime: failed to allocate memory!\n");
+ return;
+ }
+
+ } else {
+ if (num_const_boottime_list < NUM_STATIC_BOOTTIME_ENTRIES) {
+ b = &const_boottime_list[num_const_boottime_list];
+ num_const_boottime_list++;
+ } else {
+ printk(KERN_ERR
+ "boottime: too many early measure points!\n");
+ return;
+ }
+ }
+
+ INIT_LIST_HEAD(&b->list);
+
+ if (symbolic == BOOTTIME_SYMBOLIC_PRINT)
+ snprintf(b->name, BOOTTIME_MAX_NAME_LEN, "%pF", name);
+ else
+ strncpy(b->name, name, BOOTTIME_MAX_NAME_LEN);
+
+ b->name[BOOTTIME_MAX_NAME_LEN - 1] = '\0';
+ b->time = time;
+ b->cpu_load = cpu_load;
+
+ if (cpu_load == BOOTTIME_CPU_LOAD && system_up)
+ for_each_possible_cpu(i) {
+ b->cpu_usage[i].system = kstat_cpu(i).cpustat.system;
+ b->cpu_usage[i].idle = kstat_cpu(i).cpustat.idle;
+ b->cpu_usage[i].iowait = kstat_cpu(i).cpustat.iowait;
+ b->cpu_usage[i].irq = kstat_cpu(i).cpustat.irq;
+ /*
+ * TODO: Make sure that user, nice, softirq, steal
+ * and guest are not used during boot
+ */
+ }
+ else
+ b->cpu_load = BOOTTIME_NO_CPU_LOAD;
+
+ if (system_up) {
+ spin_lock_irqsave(&boottime_list_lock, flags);
+ list_add(&b->list, &boottime_list);
+ spin_unlock_irqrestore(&boottime_list_lock, flags);
+ } else {
+ list_add(&b->list, &boottime_list);
+ }
+}
+
+void __init boottime_mark_wtime(char *name, unsigned long time)
+{
+ boottime_mark_core(name, time,
+ BOOTTIME_NORMAL_PRINT,
+ BOOTTIME_NO_CPU_LOAD);
+}
+
+void __ref boottime_mark_symbolic(void *name)
+{
+
+ if (boottime_done)
+ return;
+
+ if (boottime_timer.get_time)
+ boottime_mark_core((char *) name,
+ boottime_timer.get_time(),
+ BOOTTIME_SYMBOLIC_PRINT,
+ BOOTTIME_CPU_LOAD);
+}
+
+void __init boottime_mark(char *name)
+{
+ if (boottime_timer.get_time)
+ boottime_mark_core(name,
+ boottime_timer.get_time(),
+ BOOTTIME_NORMAL_PRINT,
+ BOOTTIME_CPU_LOAD);
+}
+
+void __init boottime_activate(struct boottime_timer *bt)
+{
+ struct boottime_list *b;
+ int res = 0;
+ unsigned long flags;
+
+ if (bt == NULL) {
+ printk(KERN_ERR
+ "boottime: error: bad configured\n");
+ return;
+ }
+
+ if (bt->get_time == NULL) {
+ printk(KERN_ERR
+ "boottime: error: you must provide a get_time() function\n");
+ return;
+ }
+ memcpy(&boottime_timer, bt, sizeof(struct boottime_timer));
+
+ if (boottime_timer.init)
+ res = boottime_timer.init();
+
+ if (res) {
+ printk(KERN_ERR "boottime: initialization failed\n");
+ return;
+ }
+
+ if (boottime_arch_startup())
+ printk(KERN_ERR
+ "boottime: arch specfic initialization failed\n");
+
+ spin_lock_irqsave(&boottime_list_lock, flags);
+
+ if (!list_empty(&boottime_list)) {
+
+ b = list_first_entry(&boottime_list, struct boottime_list,
+ list);
+ if (b)
+ time_bootloader_done = b->time;
+ }
+
+ spin_unlock_irqrestore(&boottime_list_lock, flags);
+}
+
+void __init boottime_system_up(void)
+{
+ system_up = true;
+}
+
+void __init boottime_deactivate(void)
+{
+ struct boottime_list *b;
+ unsigned long flags;
+
+ boottime_mark("execute_init+0x0/0x0");
+
+ boottime_done = true;
+
+ spin_lock_irqsave(&boottime_list_lock, flags);
+ b = list_first_entry(&boottime_list, struct boottime_list, list);
+ spin_unlock_irqrestore(&boottime_list_lock, flags);
+
+ time_kernel_done = b->time;
+
+ if (boottime_timer.finalize)
+ boottime_timer.finalize();
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void boottime_debugfs_load(struct seq_file *s,
+ struct boottime_list *b,
+ struct boottime_list *p)
+{
+ int i;
+ unsigned long total_p, total_b;
+ unsigned long system_total, idle_total, irq_total, iowait_total;
+ unsigned long system_load, idle_load, irq_load, iowait_load;
+
+ for_each_possible_cpu(i) {
+ total_b = (b->cpu_usage[i].system +
+ b->cpu_usage[i].idle +
+ b->cpu_usage[i].iowait +
+ b->cpu_usage[i].irq);
+
+ total_p = (p->cpu_usage[i].system +
+ p->cpu_usage[i].idle +
+ p->cpu_usage[i].iowait +
+ p->cpu_usage[i].irq);
+
+ if (total_b == total_p)
+ continue;
+
+ system_total = b->cpu_usage[i].system - p->cpu_usage[i].system;
+ idle_total = b->cpu_usage[i].idle - p->cpu_usage[i].idle;
+ irq_total = b->cpu_usage[i].irq - p->cpu_usage[i].irq;
+ iowait_total = b->cpu_usage[i].iowait - p->cpu_usage[i].iowait;
+
+ system_load = (100 * system_total / (total_b - total_p));
+ idle_load = (100 * idle_total / (total_b - total_p));
+ irq_load = (100 * irq_total / (total_b - total_p));
+ iowait_load = (100 * iowait_total / (total_b - total_p));
+
+ seq_printf(s,
+ " cpu%d system: %lu%% idle: %lu%% iowait: %lu%% irq: %lu%%",
+ i,
+ system_load,
+ idle_load,
+ iowait_load,
+ irq_load);
+ }
+ seq_printf(s, "\n");
+}
+
+static void boottime_debugfs_print(struct seq_file *s,
+ struct boottime_list *b,
+ struct boottime_list *p)
+{
+ seq_printf(s, "[%5lu.%06lu] calling %s\n",
+ p->time / 1000000,
+ (p->time % 1000000),
+ p->name);
+ seq_printf(s, "[%5lu.%06lu] initcall %s returned 0 after %ld msecs.",
+ b->time / 1000000,
+ (b->time % 1000000),
+ p->name, (b->time - p->time) / 1000);
+
+ if (p->cpu_load == BOOTTIME_NO_CPU_LOAD ||
+ b->cpu_load == BOOTTIME_NO_CPU_LOAD) {
+ seq_printf(s, "\n");
+ return;
+ }
+
+ boottime_debugfs_load(s, b, p);
+}
+
+static int boottime_debugfs_bootgraph_show(struct seq_file *s, void *iter)
+{
+ struct boottime_list *b, *p = NULL, *old_p = NULL;
+ enum boottime_filter_type filter = (int)s->private;
+
+ list_for_each_entry_reverse(b, &boottime_list, list) {
+ if (p) {
+ if (!(filter == BOOTTIME_FILTER_OUT_ZERO &&
+ (b->time - p->time) / 1000 == 0)
+ && !(filter == BOOTTIME_FILTER_OUT_LESS_100 &&
+ (b->time - p->time) < 100 * 1000))
+ boottime_debugfs_print(s, b, p);
+ old_p = p;
+ }
+ p = b;
+ }
+
+ if (filter == BOOTTIME_FILTER_NOTHING && p)
+ boottime_debugfs_print(s, p, p);
+
+ if (p)
+ seq_printf(s, "[%5lu.%06lu] Freeing init memory: 0K\n",
+ p->time / 1000000, p->time % 1000000);
+ return 0;
+}
+
+static int boottime_debugfs_summary_show(struct seq_file *s, void *data)
+{
+ struct boottime_list *b, b_zero;
+
+ if (time_bootloader_done)
+ seq_printf(s, "bootloader: %ld msecs\n",
+ time_bootloader_done / 1000);
+
+ seq_printf(s, "kernel: %ld msecs\ntotal: %ld msecs\n",
+ (time_kernel_done - time_bootloader_done) / 1000,
+ time_kernel_done / 1000);
+ seq_printf(s, "kernel:");
+ b = list_first_entry(&boottime_list,
+ struct boottime_list, list);
+ memset(&b_zero, 0, sizeof(struct boottime_list));
+ boottime_debugfs_load(s, b, &b_zero);
+
+ if (time_bootloader_done)
+ seq_printf(s,
+ "bootloader: cpu0 system: %d%% idle: %d%% iowait: 0%% irq: 0%%\n",
+ 100 - boottime_bootloader_idle(),
+ boottime_bootloader_idle());
+ return 0;
+}
+
+static int boottime_debugfs_bootgraph_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file,
+ boottime_debugfs_bootgraph_show,
+ inode->i_private);
+}
+
+static int boottime_debugfs_summary_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file,
+ boottime_debugfs_summary_show,
+ inode->i_private);
+}
+
+static const struct file_operations boottime_debugfs_bootgraph_operations = {
+ .open = boottime_debugfs_bootgraph_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations boottime_debugfs_summary_operations = {
+ .open = boottime_debugfs_summary_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+void boottime_debugfs_init(void)
+{
+ struct dentry *dir;
+
+ dir = debugfs_create_dir("boottime", NULL);
+
+ (void) debugfs_create_file("bootgraph", S_IFREG | S_IRUGO,
+ dir, (void *)BOOTTIME_FILTER_NOTHING,
+ &boottime_debugfs_bootgraph_operations);
+ (void) debugfs_create_file("bootgraph_all_except0", S_IFREG | S_IRUGO,
+ dir, (void *)BOOTTIME_FILTER_OUT_ZERO,
+ &boottime_debugfs_bootgraph_operations);
+ (void) debugfs_create_file("bootgraph_larger100",
+ S_IFREG | S_IRUGO,
+ dir, (void *)BOOTTIME_FILTER_OUT_LESS_100,
+ &boottime_debugfs_bootgraph_operations);
+ (void) debugfs_create_file("summary", S_IFREG | S_IRUGO,
+ dir, NULL,
+ &boottime_debugfs_summary_operations);
+}
+#else
+#define boottime_debugfs_init(x)
+#endif
+
+static ssize_t show_bootloader(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%ld\n", time_bootloader_done / 1000);
+}
+
+static ssize_t show_kernel(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%ld\n",
+ (time_kernel_done - time_bootloader_done) / 1000);
+}
+
+DEVICE_ATTR(kernel, 0444, show_kernel, NULL);
+DEVICE_ATTR(bootloader, 0444, show_bootloader, NULL);
+
+static struct attribute *boottime_sysfs_entries[] = {
+ &dev_attr_kernel.attr,
+ &dev_attr_bootloader.attr,
+ NULL
+};
+
+static struct attribute_group boottime_attr_grp = {
+ .name = NULL,
+ .attrs = boottime_sysfs_entries,
+};
+
+static int __init boottime_init(void)
+{
+ struct kobject *boottime_kobj;
+
+ boottime_kobj = kobject_create_and_add("boottime", NULL);
+ if (!boottime_kobj) {
+ printk(KERN_ERR "boottime: out of memory!\n");
+ return -ENOMEM;
+ }
+
+ if (sysfs_create_group(boottime_kobj, &boottime_attr_grp) < 0) {
+ kobject_put(boottime_kobj);
+ printk(KERN_ERR "boottime: Failed creating sysfs group\n");
+ return -ENOMEM;
+ }
+
+ boottime_debugfs_init();
+
+ return 0;
+}
+
+late_initcall(boottime_init);
diff --git a/init/main.c b/init/main.c
index 217ed23e948..a59775f4557 100644
--- a/init/main.c
+++ b/init/main.c
@@ -68,6 +68,7 @@
#include <linux/shmem_fs.h>
#include <linux/slab.h>
#include <linux/perf_event.h>
+#include <linux/boottime.h>
#include <asm/io.h>
#include <asm/bugs.h>
@@ -682,6 +683,8 @@ int __init_or_module do_one_initcall(initcall_t fn)
int count = preempt_count();
int ret;
+ boottime_mark_symbolic(fn);
+
if (initcall_debug)
ret = do_one_initcall_debug(fn);
else
@@ -758,6 +761,7 @@ static noinline int init_post(void)
{
/* need to finish all async __init code before freeing the memory */
async_synchronize_full();
+ boottime_deactivate();
free_initmem();
mark_rodata_ro();
system_state = SYSTEM_RUNNING;
@@ -813,6 +817,7 @@ static int __init kernel_init(void * unused)
do_pre_smp_initcalls();
lockup_detector_init();
+ boottime_system_up();
smp_init();
sched_init_smp();
@@ -835,6 +840,7 @@ static int __init kernel_init(void * unused)
if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
ramdisk_execute_command = NULL;
+ boottime_mark("mount+0x0/0x0");
prepare_namespace();
}
diff --git a/kernel/printk.c b/kernel/printk.c
index 7982a0a841e..2cb19092743 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -41,6 +41,7 @@
#include <linux/cpu.h>
#include <linux/notifier.h>
#include <linux/rculist.h>
+#include <trace/stm.h>
#include <asm/uaccess.h>
@@ -53,6 +54,10 @@ void asmlinkage __attribute__((weak)) early_printk(const char *fmt, ...)
#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
+#ifdef CONFIG_PRINTK_LL
+extern void printascii(char *);
+#endif
+
/* printk's without a loglevel use this.. */
#define DEFAULT_MESSAGE_LOGLEVEL CONFIG_DEFAULT_MESSAGE_LOGLEVEL
@@ -876,6 +881,10 @@ asmlinkage int vprintk(const char *fmt, va_list args)
printed_len += vscnprintf(printk_buf + printed_len,
sizeof(printk_buf) - printed_len, fmt, args);
+#ifdef CONFIG_PRINTK_LL
+ printascii(printk_buf);
+#endif
+
p = printk_buf;
/* Read log level and handle special printk prefix */
@@ -897,6 +906,9 @@ asmlinkage int vprintk(const char *fmt, va_list args)
}
}
+ /* Send printk buffer to MIPI STM trace hardware too if enable */
+ stm_dup_printk(printk_buf, printed_len);
+
/*
* Copy the output into log_buf. If the caller didn't provide
* the appropriate log prefix, we insert them here
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index f2bd275bb60..8928afea2f4 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -37,6 +37,7 @@
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/fs.h>
+#include <trace/stm.h>
#include "trace.h"
#include "trace_output.h"
@@ -909,7 +910,7 @@ void tracing_reset_current_online_cpus(void)
tracing_reset_online_cpus(&global_trace);
}
-#define SAVED_CMDLINES 128
+#define SAVED_CMDLINES 2048
#define NO_CMDLINE_MAP UINT_MAX
static unsigned map_pid_to_cmdline[PID_MAX_DEFAULT+1];
static unsigned map_cmdline_to_pid[SAVED_CMDLINES];
@@ -1237,6 +1238,8 @@ trace_function(struct trace_array *tr,
if (!filter_check_discard(call, entry, buffer, event))
ring_buffer_unlock_commit(buffer, event);
+
+ stm_ftrace(ip, parent_ip);
}
void
@@ -1331,6 +1334,8 @@ static void __ftrace_trace_stack(struct ring_buffer *buffer,
if (!filter_check_discard(call, entry, buffer, event))
ring_buffer_unlock_commit(buffer, event);
+ stm_stack_trace(trace.entries);
+
out:
/* Again, don't let gcc optimize things here */
barrier();
@@ -1501,6 +1506,8 @@ int trace_vbprintk(unsigned long ip, const char *fmt, va_list args)
ftrace_trace_stack(buffer, flags, 6, pc);
}
+ stm_trace_bprintk_buf(ip, fmt, trace_buf, sizeof(u32) * len);
+
out_unlock:
arch_spin_unlock(&trace_buf_lock);
local_irq_restore(flags);
@@ -1577,6 +1584,8 @@ int trace_array_vprintk(struct trace_array *tr,
ftrace_trace_stack(buffer, irq_flags, 6, pc);
}
+ stm_trace_printk_buf(ip, trace_buf, len);
+
out_unlock:
arch_spin_unlock(&trace_buf_lock);
raw_local_irq_restore(irq_flags);
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index c212a7f934e..773cb84adc8 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -19,6 +19,7 @@
#include <linux/delay.h>
#include <asm/setup.h>
+#include <trace/stm.h>
#include "trace_output.h"
@@ -1703,6 +1704,8 @@ function_test_events_call(unsigned long ip, unsigned long parent_ip)
trace_nowake_buffer_unlock_commit(buffer, event, flags, pc);
+ stm_ftrace(ip, parent_ip);
+
out:
atomic_dec(&per_cpu(ftrace_test_event_disable, cpu));
preempt_enable_notrace();
diff --git a/kernel/trace/trace_sched_switch.c b/kernel/trace/trace_sched_switch.c
index 7e62c0a1845..a136fd86533 100644
--- a/kernel/trace/trace_sched_switch.c
+++ b/kernel/trace/trace_sched_switch.c
@@ -11,6 +11,7 @@
#include <linux/uaccess.h>
#include <linux/ftrace.h>
#include <trace/events/sched.h>
+#include <trace/stm.h>
#include "trace.h"
@@ -47,6 +48,10 @@ tracing_sched_switch_trace(struct trace_array *tr,
if (!filter_check_discard(call, entry, buffer, event))
trace_buffer_unlock_commit(buffer, event, flags, pc);
+
+ stm_sched_switch(entry->prev_pid, entry->prev_prio, entry->prev_state,
+ entry->next_pid, entry->next_prio, entry->next_state,
+ entry->next_cpu);
}
static void
@@ -103,6 +108,11 @@ tracing_sched_wakeup_trace(struct trace_array *tr,
if (!filter_check_discard(call, entry, buffer, event))
ring_buffer_unlock_commit(buffer, event);
+
+ stm_sched_wakeup(entry->prev_pid, entry->prev_prio, entry->prev_state,
+ entry->next_pid, entry->next_prio, entry->next_state,
+ entry->next_cpu);
+
ftrace_trace_stack(tr->buffer, flags, 6, pc);
ftrace_trace_userstack(tr->buffer, flags, pc);
}