From 4329df0b3960e90e69d416be2c9683568aa4cbca Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Thu, 20 Oct 2011 09:54:52 +0200 Subject: STM: MIPI System Trace Module char driver Change-Id: I5a53335d41ac1ecda02dc61d68a418a0039ebdff Signed-off-by: Philippe Langlais Signed-off-by: Robert Marklund --- Documentation/trace/stm-trace.txt | 193 +++++++++ drivers/misc/Kconfig.stm | 114 ++++++ drivers/misc/stm.c | 797 ++++++++++++++++++++++++++++++++++++++ include/Kbuild | 1 + include/trace/Kbuild | 1 + include/trace/stm.h | 223 +++++++++++ 6 files changed, 1329 insertions(+) create mode 100644 Documentation/trace/stm-trace.txt create mode 100644 drivers/misc/Kconfig.stm create mode 100644 drivers/misc/stm.c create mode 100644 include/trace/Kbuild create mode 100644 include/trace/stm.h 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 + Philippe Langlais + 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 & 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 + +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 + +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 for ST-Ericsson. + * Philippe Langlais for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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<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/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..73048bc97c5 --- /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 for ST-Ericsson. + * Philippe Langlais 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 char *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 */ -- cgit v1.2.3 From 1d45dca43032ec03a9ef39be986076839379968b Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Mon, 14 Mar 2011 15:40:02 +0100 Subject: trace: add STM trace hooks for printk, trace_printk, trace_function, stack trace and scheduler wakeup/context switch tracing Change-Id: I6621cfceccc4f000e9196d2d8909f0493d5d4774 Signed-off-by: Philippe Langlais Signed-off-by: Robert Marklund --- kernel/printk.c | 4 ++++ kernel/trace/trace.c | 9 +++++++++ kernel/trace/trace_events.c | 3 +++ kernel/trace/trace_sched_switch.c | 10 ++++++++++ 4 files changed, 26 insertions(+) diff --git a/kernel/printk.c b/kernel/printk.c index 28a40d8171b..43d88e80c9e 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -41,6 +41,7 @@ #include #include #include +#include #include @@ -897,6 +898,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 e5df02c69b1..5966ba6a1ea 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "trace.h" #include "trace_output.h" @@ -1236,6 +1237,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 @@ -1330,6 +1333,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(); @@ -1500,6 +1505,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); @@ -1576,6 +1583,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 581876f9f38..b066c6dd41a 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -19,6 +19,7 @@ #include #include +#include #include "trace_output.h" @@ -1704,6 +1705,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 #include #include +#include #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); } -- cgit v1.2.3 From 9f84e977b522b12091af7dc4d49b12a132add54c Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 9 May 2005 14:10:26 -0700 Subject: ARM: Make low-level printk work Makes low-level printk work. Signed-off-by: Tony Lindgren Signed-off-by: Mian Yousaf Kaukab --- kernel/printk.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/printk.c b/kernel/printk.c index 43d88e80c9e..0ebd5fff9c5 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -54,6 +54,10 @@ void asmlinkage __attribute__((weak)) early_printk(const char *fmt, ...) #define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT) +#ifdef CONFIG_DEBUG_LL +extern void printascii(char *); +#endif + /* printk's without a loglevel use this.. */ #define DEFAULT_MESSAGE_LOGLEVEL CONFIG_DEFAULT_MESSAGE_LOGLEVEL -- cgit v1.2.3 From 6471dd2935b5ae9f3766e3890bf88328b8c4f69c Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Thu, 11 Mar 2010 08:40:51 +0530 Subject: arm: add printascii in printk option Signed-off-by: Rabin Vincent Change-Id: Ie73cf9782ccbf3a974383fa5d1474613c660c6b9 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/3172 Tested-by: Mian Yousaf KAUKAB Reviewed-by: Srinidhi KASAGAR Signed-off-by: Mian Yousaf Kaukab --- arch/arm/Kconfig.debug | 9 +++++++++ kernel/printk.c | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug index 81cbe40c159..5816849fa15 100644 --- a/arch/arm/Kconfig.debug +++ b/arch/arm/Kconfig.debug @@ -80,6 +80,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 DEBUG_ICEDCC bool "Kernel low-level debugging via EmbeddedICE DCC channel" depends on DEBUG_LL diff --git a/kernel/printk.c b/kernel/printk.c index 0ebd5fff9c5..bbae2a8e8f3 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -54,7 +54,7 @@ void asmlinkage __attribute__((weak)) early_printk(const char *fmt, ...) #define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT) -#ifdef CONFIG_DEBUG_LL +#ifdef CONFIG_PRINTK_LL extern void printascii(char *); #endif -- cgit v1.2.3 From a1675c6dd73ab03465722e5a06f41d530ea507c4 Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Mon, 14 Feb 2011 09:51:56 +0100 Subject: ftrace: Increase number of named processes The default setting of 128 is not close to enough to save all the names of processes/threads executed during 1s of idle in Android. ST-Ericsson Linux next: - ST-Ericsson ID: - ST-Ericsson FOSS-OUT ID: Trivial Signed-off-by: Jonas Aaberg Change-Id: I6f9ef8d5f167f4b1f8de7b996130e65ee5fc7598 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/15157 Reviewed-by: Martin PERSSON Reviewed-by: Mattias WALLIN --- kernel/trace/trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 5966ba6a1ea..e7aa00caa38 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -909,7 +909,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]; -- cgit v1.2.3 From 0f66612c3c3fb92b5363d9f10aee29ca45ebb652 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Thu, 5 May 2011 08:53:16 +0200 Subject: stm: warning fix Signed-off-by: Philippe Langlais --- include/trace/stm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/trace/stm.h b/include/trace/stm.h index 73048bc97c5..72e9136c25e 100644 --- a/include/trace/stm.h +++ b/include/trace/stm.h @@ -148,7 +148,7 @@ static inline int stm_trace_printk_buf( } static inline int stm_trace_bprintk_buf( - unsigned long ip, const char *fmt, const char *buf, size_t size) + unsigned long ip, const char *fmt, const void *buf, size_t size) { return 0; } -- cgit v1.2.3 From 0923af4f59b804e7da263bb9273072d0eb234dc5 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Thu, 20 Oct 2011 10:50:37 +0200 Subject: ARM: ux500: hwreg debugfs: access DB8500 registers from debugfs entries Patch used for test purpose only. It is not plan to mainline it. Create debugfs entries to read/write specific DB8500 physical addresses. This is quite useless since devmem allows this userland access. However, debugfs entries are easier for scripting purpose. Check the debugfs entries usage from heading comments in hwreg.c ST-Ericsson Linux next: Not to be mainlined ST-Ericsson ID: - ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Id202562030a144d5c2c1aaa3e67e7f4de60c2cb2 Signed-off-by: carriere etienne Signed-off-by: Jonas Aaberg Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/17831 --- arch/arm/mach-ux500/hwreg.c | 660 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 660 insertions(+) create mode 100644 arch/arm/mach-ux500/hwreg.c diff --git a/arch/arm/mach-ux500/hwreg.c b/arch/arm/mach-ux500/hwreg.c new file mode 100644 index 00000000000..7ba3e0f2911 --- /dev/null +++ b/arch/arm/mach-ux500/hwreg.c @@ -0,0 +1,660 @@ +/* + * Copyright (C) 2011 ST-Ericsson SA + * + * Author: Etienne CARRIERE 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 > /mem/reg-addr + * # cat /mem/reg-val + * + * 32bit write: + * # echo > /mem/reg-addr + * # echo > /mem/reg-val + * + * 0x-prefixed hexadecimal + * 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< [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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +/* + * 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) +{ + char buf[32]; + int buf_size; + unsigned long user_address; + + /* 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; + + if(strict_strtoul(buf, 0, &user_address)) + return -EINVAL; + if (hwreg_io_ptov(user_address)==NULL) + return -EADDRNOTAVAIL; + + debug_address = user_address; + return buf_size; +} + +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) +{ + char buf[32]; + int buf_size; + unsigned long user_val; + void *ptr; + + /* 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; + + if (strict_strtoul(buf, 0, &user_val)) + return -EINVAL; + + if ((ptr = hwreg_io_ptov(debug_address)) == NULL) + return -EFAULT; + writel(user_val, ptr); + return buf_size; +} + +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"); -- cgit v1.2.3 From 2522c386fddae74bf92991c1a4089b0506148664 Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Wed, 30 Jun 2010 14:00:40 +0200 Subject: Boottime - A tool for automatic measurement of kernel and possible boot loader boot time. The overhead is very low and the results will be found under sysfs/bootime, as well as detailed results in debugfs under boottime/. The bootgraph* files are compatible with scripts/bootgraph.pl. The reason for this patch is to provide data (sysfs/boottime) suitable for automatic testcases as well as help for developers to reduce the boot time (debugfs). Signed-off-by: Jonas Aaberg Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/2760 Change-Id: Id8ea7226b3f1f783448962195193b90699d32fc6 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/3174 Tested-by: Mian Yousaf KAUKAB Reviewed-by: Srinidhi KASAGAR Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Lee Jones --- arch/arm/common/Makefile | 1 + arch/arm/common/boottime.c | 46 +++++ arch/arm/include/asm/setup.h | 21 ++ include/linux/boottime.h | 89 +++++++++ init/Kconfig | 9 + init/Makefile | 1 + init/boottime.c | 467 +++++++++++++++++++++++++++++++++++++++++++ init/main.c | 6 + 8 files changed, 640 insertions(+) create mode 100644 arch/arm/common/boottime.c create mode 100644 include/linux/boottime.h create mode 100644 init/boottime.c 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 for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * Store boot times measured during for example u-boot startup. + */ + +#include +#include +#include +#include +#include + +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/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 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 + +/** + * 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/init/Kconfig b/init/Kconfig index d62778390e5..1dca5b04fcd 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1273,6 +1273,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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 03b408dff82..925b467ec55 100644 --- a/init/main.c +++ b/init/main.c @@ -68,6 +68,7 @@ #include #include #include +#include #include #include @@ -679,6 +680,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 @@ -755,6 +758,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; @@ -810,6 +814,7 @@ static int __init kernel_init(void * unused) do_pre_smp_initcalls(); lockup_detector_init(); + boottime_system_up(); smp_init(); sched_init_smp(); @@ -832,6 +837,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(); } -- cgit v1.2.3 From 6829f09509fc7e0b17753c48f7eea4c126df7ac3 Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Mon, 20 Jun 2011 12:09:38 +0200 Subject: ARM: ux500: hwreg: Tiny code clean-up ST-Ericsson Linux next: Not tested, ask SSM for ER ST-Ericsson ID: - ST-Ericsson FOSS-OUT ID: Trivial Signed-off-by: Jonas Aaberg Change-Id: I5b7331fade22ae99dc2600d67b41bf286179cee8 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/25489 Reviewed-by: QATEST Reviewed-by: Linus WALLEIJ --- arch/arm/mach-ux500/hwreg.c | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/arch/arm/mach-ux500/hwreg.c b/arch/arm/mach-ux500/hwreg.c index 7ba3e0f2911..1a47e60ed46 100644 --- a/arch/arm/mach-ux500/hwreg.c +++ b/arch/arm/mach-ux500/hwreg.c @@ -300,23 +300,19 @@ static int hwreg_address_open(struct inode *inode, struct file *file) static ssize_t hwreg_address_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { - char buf[32]; - int buf_size; + int err; unsigned long user_address; - /* 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; + err = kstrtoul_from_user(user_buf, count, 0, &user_address); + + if (err) + return err; - if(strict_strtoul(buf, 0, &user_address)) - return -EINVAL; if (hwreg_io_ptov(user_address)==NULL) return -EADDRNOTAVAIL; debug_address = user_address; - return buf_size; + return count; } static int hwreg_value_print(struct seq_file *s, void *p) @@ -338,24 +334,19 @@ static int hwreg_value_open(struct inode *inode, struct file *file) static ssize_t hwreg_value_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { - char buf[32]; - int buf_size; + int err; unsigned long user_val; void *ptr; - /* 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; + err = kstrtoul_from_user(user_buf, count, 0, &user_val); - if (strict_strtoul(buf, 0, &user_val)) - return -EINVAL; + if (err) + return err; if ((ptr = hwreg_io_ptov(debug_address)) == NULL) return -EFAULT; writel(user_val, ptr); - return buf_size; + return count; } static const struct file_operations hwreg_address_fops = { -- cgit v1.2.3 From 8d092e972ed6fb25a41a7d991020a0161b5538dd Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Fri, 26 Aug 2011 09:56:23 +0530 Subject: printk: don't use CONFIG_DEBUG_LL Change-Id: I1e3cf9a0e67c7941ec9a276678dcb61c01a2a957 --- kernel/printk.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/printk.c b/kernel/printk.c index bbae2a8e8f3..5545d40dbcd 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -881,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 */ -- cgit v1.2.3 From 6f4064452f2b0c106a5ffe17b987a3f6d73b79ac Mon Sep 17 00:00:00 2001 From: Jurijs Soloveckis Date: Mon, 3 Oct 2011 15:03:39 +0200 Subject: ARM: u8500: STM: board depended code split Hardware depended code for STM device is moved to the separate file. ST-Ericsson ID: 349677 ST-Ericsson Linux next: N/A ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I5d3d0e069b0e8929b99fcfe9cb5e37a950caf716 Signed-off-by: Jurijs Soloveckis Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32847 Reviewed-by: Pierre PEIFFER Reviewed-by: Sebastian RASMUSSEN Signed-off-by: Robert Marklund --- arch/arm/mach-ux500/board-mop500-stm.c | 203 +++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 arch/arm/mach-ux500/board-mop500-stm.c 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..8a0fc83679d --- /dev/null +++ b/arch/arm/mach-ux500/board-mop500-stm.c @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2011 ST-Ericsson + * + * Author: Pierre Peiffer for ST-Ericsson. + * Author: Olivier Germain + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#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, + }, +}; -- cgit v1.2.3 From 38693f25c9a8aa303ef6f1e39ad95de2ea88060b Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Thu, 17 Nov 2011 15:32:50 +0100 Subject: misc: stm: Compile System Trace Module misc driver Signed-off-by: Philippe Langlais --- drivers/misc/Kconfig | 1 + drivers/misc/Makefile | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 2d6423c2d19..a70968061d1 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -499,6 +499,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/Makefile b/drivers/misc/Makefile index 8f3efb68a14..8c044eb145e 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -46,4 +46,5 @@ 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 -- cgit v1.2.3 From 56fb255840b2bead8b31d82cc8c31a6d416be96c Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 18 Nov 2011 15:06:56 +0100 Subject: mach-ux500: STM: break bad dependency with hsi Signed-off-by: Philippe Langlais --- arch/arm/mach-ux500/board-mop500-stm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-ux500/board-mop500-stm.c b/arch/arm/mach-ux500/board-mop500-stm.c index 8a0fc83679d..7d8148c5878 100644 --- a/arch/arm/mach-ux500/board-mop500-stm.c +++ b/arch/arm/mach-ux500/board-mop500-stm.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include "pins-db8500.h" -- cgit v1.2.3