From b8d7c870bcee6b9a2e76789c47ea94b6f8f183e6 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 65439e45b74fb46d0d543f2bbae770deb733b383 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 b663c2c95d3..e9b92c6e2cb 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -41,6 +41,7 @@ #include #include #include +#include #include @@ -905,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 2a22255c101..b917e6064d4 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -38,6 +38,7 @@ #include #include #include +#include #include "trace.h" #include "trace_output.h" @@ -1293,6 +1294,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 @@ -1387,6 +1390,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(); @@ -1557,6 +1562,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); @@ -1633,6 +1640,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 29111da1d10..7dd750a3443 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -19,6 +19,7 @@ #include #include +#include #include "trace_output.h" @@ -1712,6 +1713,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 e417ab3e7bcbefb9e368b5d115823479a9f5a6d3 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 e9b92c6e2cb..9e29170e193 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -57,6 +57,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 d8e5d25dc25f7c588eda9e176465502208111e3a 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 85348a09d65..6ca82db8d21 100644 --- a/arch/arm/Kconfig.debug +++ b/arch/arm/Kconfig.debug @@ -318,6 +318,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/kernel/printk.c b/kernel/printk.c index 9e29170e193..482ba933608 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -57,7 +57,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 008934cbfc5ddae7dcf67889ef4aa1255967405b 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 b917e6064d4..dba8f56afc2 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -966,7 +966,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 97460607bd573867caed422253219fe62e558cc8 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 a4f60cc32fdd87e7cc2c85275bc4f667a6b60d56 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 bc52cfb0c0aebaed24dab77b33477a20d4e0a430 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 215816f1775..d5b5aa2fe3c 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -15,3 +15,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 23ebc0c82a3..ea1384fea05 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 6cfd71d0646..c2786394546 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1311,6 +1311,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 44b2433334c..90c0067c647 100644 --- a/init/main.c +++ b/init/main.c @@ -68,6 +68,7 @@ #include #include #include +#include #include #include @@ -678,6 +679,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 @@ -802,6 +805,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; @@ -857,6 +861,7 @@ static int __init kernel_init(void * unused) do_pre_smp_initcalls(); lockup_detector_init(); + boottime_system_up(); smp_init(); sched_init_smp(); @@ -879,6 +884,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 70994fbb103b0d8d9990669b3d3beb3359e905a9 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 9ddffde4cc217be6e108f54db566ce4560670f28 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 482ba933608..c538c187618 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -889,6 +889,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 fb79b11cc32ec8024d8fd34ba412d5d01f0a96e1 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 307e81118fc26c8e01f54af4a0fd184a619c1bd8 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 c7795096d43..7b7fc5c7489 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -498,6 +498,7 @@ config MAX8997_MUIC Maxim MAX8997 PMIC. The MAX8997 MUIC is a USB port accessory detector and switch. +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 3e1d80106f0..90f219db507 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -46,6 +46,7 @@ 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/ obj-$(CONFIG_MAX8997_MUIC) += max8997-muic.o -- cgit v1.2.3 From 5dce3732e65887c11f439e954cbf3afcd9356bbf 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 From 22e8d8ce6d22656af59b4324a99bd6735de7f613 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Wed, 11 Jan 2012 10:37:58 +0100 Subject: mach-ux500: stm: u8500: API for PRCMU_GPIOCR register The PRCMU_GPIOCR register modification within STM driver is implemented via corresponding API functions. ST-Ericsson ID: 349677 ST-Ericsson Linux next: N/A ST-Ericsson FOSS-OUT ID: Trivial Signed-off-by: Jurijs Soloveckis --- arch/arm/mach-ux500/board-mop500-stm.c | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/arch/arm/mach-ux500/board-mop500-stm.c b/arch/arm/mach-ux500/board-mop500-stm.c index 7d8148c5878..20d3f9a546b 100644 --- a/arch/arm/mach-ux500/board-mop500-stm.c +++ b/arch/arm/mach-ux500/board-mop500-stm.c @@ -77,15 +77,9 @@ static int stm_ste_disable_ape_on_mipi60(void) /* * 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 */ @@ -101,26 +95,19 @@ static int stm_ste_connection(enum stm_connection_type 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); + prcmu_enable_stm_mod_uart(); 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); + prcmu_disable_stm_mod_uart(); 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); - + prcmu_enable_stm_mod_uart(); /* Enable APE on MIPI60 */ retval = nmk_config_pins_sleep(ARRAY_AND_SIZE(mop500_ske_pins)); if (retval) -- cgit v1.2.3 From 8d9b2a925c726ad702580ced887eeb4f41f37370 Mon Sep 17 00:00:00 2001 From: Jurijs Soloveckis Date: Mon, 17 Oct 2011 10:21:00 +0200 Subject: ARM: u8500: Add control for modem and ape trace SD-card, mipi34 and mipi60 IFs can be configured for modem or ape trace, using u-boot cmd prompt. STM drv is controlled via kernel environment vars: Example: stm.microsd=[none|modem|ape], or stm.mipi34=[none|modem|ape], or stm.mipi60=[none|modem|ape] If trace set to ape, use stm.stm_ter=N to init STM_TER reg with decimal value N (default: N=0). ST-Ericsson ID: 349677 ST-Ericsson Linux next: N/A ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ie04e4c004417c4542259e3074f1fa2fd4f00ce6d Depends-On: I7bbcf263b83338d7b05413dd9dc0ac2ec4880601 Signed-off-by: Jurijs Soloveckis Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/34160 Reviewed-by: QATOOLS Reviewed-by: Jonas ABERG --- arch/arm/mach-ux500/board-mop500-regulators.c | 1 + arch/arm/mach-ux500/board-mop500-stm.c | 277 ++++++++++++++++++++++++-- drivers/misc/stm.c | 60 +++++- include/trace/stm.h | 5 +- 4 files changed, 326 insertions(+), 17 deletions(-) diff --git a/arch/arm/mach-ux500/board-mop500-regulators.c b/arch/arm/mach-ux500/board-mop500-regulators.c index 52426a42578..df34f6be694 100644 --- a/arch/arm/mach-ux500/board-mop500-regulators.c +++ b/arch/arm/mach-ux500/board-mop500-regulators.c @@ -63,6 +63,7 @@ static struct regulator_consumer_supply ab8500_vaux2_consumers[] = { }; static struct regulator_consumer_supply ab8500_vaux3_consumers[] = { + REGULATOR_SUPPLY("v-SD-STM", "stm"), /* External MMC slot power */ REGULATOR_SUPPLY("vmmc", "sdi0"), }; diff --git a/arch/arm/mach-ux500/board-mop500-stm.c b/arch/arm/mach-ux500/board-mop500-stm.c index 20d3f9a546b..d38595b11b2 100644 --- a/arch/arm/mach-ux500/board-mop500-stm.c +++ b/arch/arm/mach-ux500/board-mop500-stm.c @@ -13,13 +13,27 @@ #include #include #include +#include +#include + +#include #include #include -#include #include #include #include "pins-db8500.h" +#define HREFV60_SDMMC_EN_GPIO 169 +#define HREFV60_SDMMC_1V8_3V_GPIO 5 + +#define STM_DEVICE (&u8500_stm_device.dev) +#define STM_ERR(msg) dev_err(STM_DEVICE, msg) +#define STM_WARN(msg) dev_warn(STM_DEVICE, msg) + +static struct regulator *regulator_aux3; +static enum stm_connection_type + stm_current_connection = STM_STE_INVALID_CONNECTION; + 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, @@ -40,6 +54,15 @@ static pin_cfg_t mop500_stm_mipi60_pins[] = { GPIO159_STMAPE_DAT0, }; +static pin_cfg_t mop500_stm_ape_microsd_pins[] = { + GPIO23_MS_CLK | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP, + GPIO24_MS_BS | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP, + GPIO25_MS_DAT0 | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP, + GPIO26_MS_DAT1 | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP, + GPIO27_MS_DAT2 | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP, + GPIO28_MS_DAT3 | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP, +}; + 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, @@ -59,35 +82,211 @@ static pin_cfg_t mop500_ske_pins[] = { GPIO168_KP_O0 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW, }; +static pin_cfg_t mop500_stm_modem_microsd_pins[] = { + GPIO18_GPIO | PIN_OUTPUT_LOW, + GPIO19_GPIO | PIN_OUTPUT_HIGH, + GPIO20_GPIO | PIN_OUTPUT_HIGH, + GPIO22_GPIO | PIN_INPUT_PULLUP, + GPIO23_STMMOD_CLK | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP, + GPIO24_UARTMOD_RXD | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP, + GPIO25_STMMOD_DAT0 | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP, + GPIO26_STMMOD_DAT1 | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP, + GPIO27_STMMOD_DAT2 | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP, + GPIO28_STMMOD_DAT3 | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP, +}; + +/* sdi0 (removable MMC/SD/SDIO cards) */ +static pin_cfg_t mop500_sdi0_pins[] = { + GPIO18_MC0_CMDDIR | PIN_OUTPUT_HIGH, + GPIO19_MC0_DAT0DIR | PIN_OUTPUT_HIGH, + GPIO20_MC0_DAT2DIR | PIN_OUTPUT_HIGH, + + GPIO22_MC0_FBCLK | PIN_INPUT_NOPULL, + GPIO23_MC0_CLK | PIN_OUTPUT_LOW, + GPIO24_MC0_CMD | PIN_INPUT_PULLUP, + GPIO25_MC0_DAT0 | PIN_INPUT_PULLUP, + GPIO26_MC0_DAT1 | PIN_INPUT_PULLUP, + GPIO27_MC0_DAT2 | PIN_INPUT_PULLUP, + GPIO28_MC0_DAT3 | PIN_INPUT_PULLUP, +}; + 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"); + STM_ERR("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"); + STM_ERR("Failed to enable SKE gpio\n"); } return retval; } -/* - * Manage STM output pins connection (MIP34/MIPI60 connectors) - */ +static int stm_enable_ape_microsd(void) +{ + int retval; + + /* + * Configure STM APE on GPIO23,GPIO28,GPIO27,GPIO26,GPIO25 + * On HREF board an external SD buffer exist (ST6G3244ME) + * to perform level conversion from 1.8v to 3.3V on SD card signals + * When STM is redirected on micro SD connector GPIO18,GP19,GPIO20 + * are configured in standard GPIO mode and are used to configure + * direction on external SD buffer ST6G3244ME. + */ + + retval = nmk_config_pins(ARRAY_AND_SIZE(mop500_stm_ape_microsd_pins)); + if (retval) + STM_ERR("Failed to enable STM APE on MICRO SD\n"); + + /* Enable altC1 on GPIO23-28 (STMAPE) */ + prcmu_enable_stm_ape(); + + return retval; +} + +static int stm_disable_ape_microsd(void) +{ + int retval; + + /* Disable altC1 on GPIO23-28 (STMAPE) */ + prcmu_disable_stm_ape(); + + /* Reconfigure GPIO for SD */ + retval = nmk_config_pins_sleep(ARRAY_AND_SIZE(mop500_sdi0_pins)); + if (retval) + STM_ERR("Failed to disable STM APE on MICRO SD " + "and to reconfigure GPIO for SD\n"); + + return retval; +} + +static int stm_enable_modem_microsd(void) +{ + int retval; + + /* + * Configure STM APE on GPIO23,GPIO28,GPIO27,GPIO26,GPIO25 + * On HREF board an external SD buffer exist (ST6G3244ME) + * to perform level conversion from 1.8v to 3.3V on SD card + * signals. When STM is redirected on micro SD connector + * GPIO18,GP19,GPIO20 are configured in standard GPIO mode + * and are used to configure direction on external SD buffer + * ST6G3244ME. + */ + + retval = nmk_config_pins(ARRAY_AND_SIZE(mop500_stm_modem_microsd_pins)); + if (retval) + STM_ERR("Failed to enable STM MODEM on MICRO SD\n"); + + return retval; +} + +static int stm_disable_modem_microsd(void) +{ + int retval; + + /* Reconfigure GPIO for SD */ + retval = nmk_config_pins_sleep(ARRAY_AND_SIZE(mop500_sdi0_pins)); + if (retval) + STM_ERR("Failed to disable STM MODEM on MICRO SD " + "and to reconfigure GPIO for SD\n"); + + return retval; +} + +/* Enable or disable micro sd card buffers on HREF */ +static void control_level_shifter_for_microsd(int gpio_dir) +{ + int gpio[2]; + + if (machine_is_hrefv60()) { + gpio[0] = HREFV60_SDMMC_EN_GPIO; + gpio[1] = HREFV60_SDMMC_1V8_3V_GPIO; + } else { + gpio[0] = MOP500_EGPIO(17); + gpio[1] = MOP500_EGPIO(18); + } + + /* Select the default 2.9V and enable / disable level shifter */ + gpio_direction_output(gpio[1], 0); + gpio_direction_output(gpio[0], gpio_dir); +} + +/* Enable micro sd card buffers on HREF */ +static int enable_level_shifter_for_microsd(void) +{ + control_level_shifter_for_microsd(1); + STM_WARN("Level Shifter for SD card connector on.\n"); + return 0; +} + +/* Disable micro sd card buffers on HREF */ +static int disable_level_shifter_for_microsd(void) +{ + control_level_shifter_for_microsd(0); + STM_WARN("Level Shifter for SD card connector off.\n"); + return 0; +} + +/* Enable VAUX3 to power on buffer on STM MICRO SD cable */ +static int enable_vaux3_for_microsd_cable(void) +{ + int error; + + regulator_aux3 = regulator_get(&u8500_stm_device.dev, "v-SD-STM"); + + if (IS_ERR(regulator_aux3)) { + error = PTR_ERR(regulator_aux3); + STM_ERR("Failed to get regulator, supply: v-SD-STM\n"); + return error; + } + + error = regulator_enable(regulator_aux3); + + if (error) { + STM_ERR("Unable to enable regulator on SD card connector\n"); + return error; + } + + STM_WARN("Regulator on SD card connector power on.\n"); + return error; +} + +/* Disable VAUX3 to power off buffer on STM MICRO SD cable */ +static int disable_vaux3_for_microsd_cable(void) +{ + int error = 0; + + error = regulator_disable(regulator_aux3); + + if (regulator_aux3) + regulator_put(regulator_aux3); + + STM_WARN("Regulator for stm on SD card connector power off.\n"); + + return error; + +} + static int stm_ste_connection(enum stm_connection_type con_type) { int retval = -EINVAL; + /* Check if connection type has been changed */ + if (con_type == stm_current_connection) + return 0; + 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; + STM_ERR("Failed to enable MIPI34\n"); + goto stm_ste_connection_error; } } @@ -111,12 +310,40 @@ static int stm_ste_connection(enum stm_connection_type con_type) /* 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"); + STM_ERR("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"); + STM_ERR("Failed to enable MIPI60\n"); + } + break; + + case STM_STE_MODEM_ON_MICROSD: + /* Disable APE on micro SD */ + retval = stm_disable_ape_microsd(); + /* Enable modem on micro SD */ + if (!retval) + retval = stm_enable_modem_microsd(); + /* Enable SD card buffer and regulator on href */ + if (!retval && (stm_current_connection + != STM_STE_APE_ON_MICROSD)) { + enable_level_shifter_for_microsd(); + enable_vaux3_for_microsd_cable(); + } + break; + + case STM_STE_APE_ON_MICROSD: + /* Disable modem on micro SD */ + retval = stm_disable_modem_microsd(); + /* Enable ape on micro SD */ + if (!retval) + retval = stm_enable_ape_microsd(); + /* Enable SD card buffer and regulator on href */ + if (!retval && (stm_current_connection + != STM_STE_MODEM_ON_MICROSD)) { + enable_level_shifter_for_microsd(); + enable_vaux3_for_microsd_cable(); } break; @@ -124,15 +351,39 @@ static int stm_ste_connection(enum stm_connection_type con_type) retval = nmk_config_pins_sleep( ARRAY_AND_SIZE(mop500_stm_mipi34_pins)); if (retval) - pr_err("STM: Failed to disable MIPI34\n"); + STM_ERR("Failed to disable MIPI34\n"); retval = stm_ste_disable_ape_on_mipi60(); + if (retval) + STM_ERR("Failed to disable MIPI60\n"); + + retval = stm_disable_modem_microsd(); + if (retval) + STM_ERR("Failed to disable modem on microsd\n"); + + retval = stm_disable_ape_microsd(); + if (retval) + STM_ERR("Failed to disable ape on microsd\n"); break; default: - pr_err("STM: bad connection type\n"); - break; + STM_ERR("Bad connection type\n"); + goto stm_ste_connection_error; + } + + /* Disable power for microsd */ + if ((stm_current_connection == STM_STE_MODEM_ON_MICROSD) + || (stm_current_connection == STM_STE_APE_ON_MICROSD)) { + if ((con_type != STM_STE_MODEM_ON_MICROSD) + && (con_type != STM_STE_APE_ON_MICROSD)) { + disable_vaux3_for_microsd_cable(); + disable_level_shifter_for_microsd(); + } } + + stm_current_connection = con_type; + +stm_ste_connection_error: return retval; } diff --git a/drivers/misc/stm.c b/drivers/misc/stm.c index a05b2d14573..53c47c1693a 100644 --- a/drivers/misc/stm.c +++ b/drivers/misc/stm.c @@ -73,6 +73,33 @@ static u64 stm_printk_buf[1024/sizeof(u64)]; static arch_spinlock_t stm_buf_lock = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED; +static char *mipi60 = "none"; +module_param(mipi60, charp, S_IRUGO); +MODULE_PARM_DESC(mipi60, "STM Trace to output on probe2 of mipi60 " + "('none' or 'ape' or 'modem')"); + +static char *mipi34 = "none"; +module_param(mipi34, charp, S_IRUGO); +MODULE_PARM_DESC(mipi34, "STM Trace to output on mipi34 " + "('none' or 'ape' or 'modem')"); + +static char *microsd = "none"; +module_param(microsd, charp, S_IRUGO); +MODULE_PARM_DESC(microsd, "STM Trace to output on SD card connector " + "('none' or 'ape' or 'modem')"); + +static unsigned int stm_ter; +module_param(stm_ter, uint, 0); +MODULE_PARM_DESC(stm_ter, "Value for STM_TER (trace control register). " + "Should be set by user as environment variable stm.stm_ter"); + +#define IS_APE_ON_MIPI34 (mipi34 && !strcmp(mipi34, "ape")) +#define IS_APE_ON_MIPI60 (mipi60 && !strcmp(mipi60, "ape")) +#define IS_APE_ON_MICROSD (microsd && !strcmp(microsd, "ape")) +#define IS_MODEM_ON_MICROSD (microsd && !strcmp(microsd, "modem")) + +static int stm_connection_set(void *data, u64 val); + int stm_alloc_channel(int offset) { int channel; @@ -251,6 +278,7 @@ EXPORT_SYMBOL(stm_trace_buffer_onchannel); static int stm_open(struct inode *inode, struct file *file) { struct channel_data *channel_data; + int retval = 0; channel_data = kzalloc(sizeof(struct channel_data), GFP_KERNEL); if (channel_data == NULL) @@ -260,7 +288,19 @@ static int stm_open(struct inode *inode, struct file *file) channel_data->numero = -1; /* Channel not yet allocated */ file->private_data = channel_data; - return 0; + /* + * Check if microsd is selected as trace interface + * and enable corresponding pins muxing. + */ + if (IS_MODEM_ON_MICROSD) + retval = stm_connection_set(NULL, STM_STE_MODEM_ON_MICROSD); + else if (IS_APE_ON_MICROSD) + retval = stm_connection_set(NULL, STM_STE_APE_ON_MICROSD); + + if (retval) + pr_alert("stm_open: failed to connect STM output\n"); + + return retval; } static int stm_release(struct inode *inode, struct file *file) @@ -338,6 +378,11 @@ static void stm_enable_src(unsigned int v) cr_val = readl(STM_CR); cr_val &= ~STM_CLOCK_MASK; writel(cr_val|(stm_clockdiv<stm_connection) { retval = stm.pdata->stm_connection(stm_connection); if (retval) { @@ -661,11 +713,13 @@ static int stm_connection_show(void *data, u64 *val) static int stm_connection_set(void *data, u64 val) { + int retval = 0; + if (stm.pdata->stm_connection) { stm_connection = val; - stm.pdata->stm_connection(val); + retval = stm.pdata->stm_connection(val); } - return 0; + return retval; } DEFINE_SIMPLE_ATTRIBUTE(stm_connection_fops, stm_connection_show, diff --git a/include/trace/stm.h b/include/trace/stm.h index 72e9136c25e..3cf11143c0e 100644 --- a/include/trace/stm.h +++ b/include/trace/stm.h @@ -64,7 +64,10 @@ enum stm_connection_type { 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 + STM_STE_MODEM_ON_MIPI34_APE_ON_MIPI60 = 4, + STM_STE_MODEM_ON_MICROSD = 5, + STM_STE_APE_ON_MICROSD = 6, + STM_STE_INVALID_CONNECTION = 0xff }; #ifdef __KERNEL__ -- cgit v1.2.3 From 1cf7721618126bee8e7f025af8e05c6114675733 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Wed, 11 Jan 2012 15:17:26 +0100 Subject: stm: Add include linux/types.h Signed-off-by: Philippe Langlais --- include/trace/stm.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/trace/stm.h b/include/trace/stm.h index 3cf11143c0e..de3ed1dc381 100644 --- a/include/trace/stm.h +++ b/include/trace/stm.h @@ -11,6 +11,8 @@ #ifndef STM_H #define STM_H +#include + #define STM_DEV_NAME "stm" /* One single channel mapping */ -- cgit v1.2.3 From 045169df5cde22e97443190098394115b3c51385 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Thu, 12 Jan 2012 12:01:07 +0100 Subject: hwreg: ux500: Coding style fixes Fix the most obvious violations of the kernel coding style Signed-off-by: Jonas Aaberg --- arch/arm/mach-ux500/hwreg.c | 70 ++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/arch/arm/mach-ux500/hwreg.c b/arch/arm/mach-ux500/hwreg.c index 1a47e60ed46..f35472e3ebf 100644 --- a/arch/arm/mach-ux500/hwreg.c +++ b/arch/arm/mach-ux500/hwreg.c @@ -1,4 +1,4 @@ -/* +%/* * Copyright (C) 2011 ST-Ericsson SA * * Author: Etienne CARRIERE for ST-Ericsson @@ -98,9 +98,8 @@ #include #include #include - -#include -#include +#include +#include #include @@ -138,9 +137,9 @@ struct hwreg_cfg { }; #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 */ +#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 */ @@ -308,7 +307,7 @@ static ssize_t hwreg_address_write(struct file *file, if (err) return err; - if (hwreg_io_ptov(user_address)==NULL) + if (hwreg_io_ptov(user_address) == NULL) return -EADDRNOTAVAIL; debug_address = user_address; @@ -398,15 +397,15 @@ static const struct file_operations hwreg_map_fops = { static int hwreg_print(struct seq_file *s, void *d) { - struct hwreg_cfg *c = (struct hwreg_cfg*) s->private; + struct hwreg_cfg *c = (struct hwreg_cfg *) s->private; void *p; uint v; - if ((c==NULL) || ((p = hwreg_io_ptov(c->addr))==NULL)) + 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 = (c->shift >= 0) ? v >> c->shift : v << (-c->shift); v = v & c->mask; if (REG_FMT_DEC(c)) @@ -434,16 +433,16 @@ static int hwreg_open(struct inode *inode, struct file *file) static int strval_len(char *b) { char *s = b; - if((*s=='0') && ((*(s+1)=='x') || (*(s+1)=='X'))) { + if ((*s == '0') && ((*(s+1) == 'x') || (*(s+1) == 'X'))) { s += 2; - for (; *s && (*s!=' ') && (*s!='\n'); s++) { + for (; *s && (*s != ' ') && (*s != '\n'); s++) { if (!isxdigit(*s)) return 0; } } else { - if (*s=='-') + if (*s == '-') s++; - for (; *s && (*s!=' ') && (*s!='\n'); s++) { + for (; *s && (*s != ' ') && (*s != '\n'); s++) { if (!isdigit(*s)) return 0; } @@ -457,7 +456,7 @@ static int strval_len(char *b) */ static ssize_t hwreg_common_write(char *b, struct hwreg_cfg *cfg) { - uint write, val=0, offset=0; + uint write, val = 0, offset = 0; struct hwreg_cfg loc = { .addr = 0, /* default: invalid phys addr */ .fmt = 0, /* default: 32bit access, hex output */ @@ -466,7 +465,7 @@ static ssize_t hwreg_common_write(char *b, struct hwreg_cfg *cfg) }; /* read or write ? */ - if(!strncmp(b, "read ", 5)) { + if (!strncmp(b, "read ", 5)) { write = 0; b += 5; } else if (!strncmp(b, "write ", 6)) { @@ -477,31 +476,31 @@ static ssize_t hwreg_common_write(char *b, struct hwreg_cfg *cfg) } /* OPTIONS -l|-w|-b -s -m -o */ - while((*b==' ') || (*b=='-')) { - if (*(b-1)!=' ') { + while ((*b == ' ') || (*b == '-')) { + if (*(b-1) != ' ') { b++; continue; } if ((!strncmp(b, "-d ", 3)) || (!strncmp(b, "-dec ", 5))) { - b += (*(b+2)==' ') ? 3 : 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; + 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) + 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) + } 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) + } 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)) { @@ -518,7 +517,7 @@ static ssize_t hwreg_common_write(char *b, struct hwreg_cfg *cfg) } } /* get arg ADDRESS */ - if (strval_len(b)==0) + if (strval_len(b) == 0) return -EINVAL; loc.addr = simple_strtoul(b, &b, 0); loc.addr += offset; @@ -526,8 +525,9 @@ static ssize_t hwreg_common_write(char *b, struct hwreg_cfg *cfg) return -EINVAL; if (write) { - while(*b==' ') b++; /* skip spaces up to arg VALUE */ - if (strval_len(b)==0) + while (*b == ' ') + b++; /* skip spaces up to arg VALUE */ + if (strval_len(b) == 0) return -EINVAL; val = simple_strtoul(b, &b, 0); } @@ -538,9 +538,9 @@ static ssize_t hwreg_common_write(char *b, struct hwreg_cfg *cfg) #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", + (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 -- cgit v1.2.3 From 5e6c28b8714b930484fd216326d0acec89b4a57c Mon Sep 17 00:00:00 2001 From: Bibek Basu Date: Mon, 28 Nov 2011 15:58:00 +0530 Subject: u8500 : misc : stm driver not functional configure stm mode to software to fix the broken driver ST-Ericsson Linux next: NA ST-Ericsson ID: 373611 ST-Ericsson FOSS-OUT ID: Trivial Signed-off-by: Bibek Basu Change-Id: Ib1349cfea5a4d3143d232a0ff3fe1e93820c6439 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/40440 Reviewed-by: QABUILD Reviewed-by: Anil KUMAR (STE) Reviewed-by: Philippe LANGLAIS --- drivers/misc/Kconfig.stm | 6 ++++++ drivers/misc/stm.c | 3 +-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/misc/Kconfig.stm b/drivers/misc/Kconfig.stm index ef2b94683a3..d509c85c79f 100644 --- a/drivers/misc/Kconfig.stm +++ b/drivers/misc/Kconfig.stm @@ -14,6 +14,12 @@ config STM_NUMBER_OF_CHANNEL help Number Max of channels always a multiple of 256 +config STM_DEFAULT_MASTERS_MODES + hex "channel mode" + default 0xffffffff + help + Default config for enabling hardware mode tracing + config STM_PRINTK bool "printk support" depends on STM_TRACE diff --git a/drivers/misc/stm.c b/drivers/misc/stm.c index 53c47c1693a..33bb26c27ca 100644 --- a/drivers/misc/stm.c +++ b/drivers/misc/stm.c @@ -32,7 +32,7 @@ #define STM_CLOCK_MASK 0x1C0 /* Hardware mode for all sources */ -#define STM_MMC_DEFAULT 0xFFFFFFFF +#define STM_MMC_DEFAULT CONFIG_STM_DEFAULT_MASTERS_MODES /* Max number of channels (multiple of 256) */ #define STM_NUMBER_OF_CHANNEL CONFIG_STM_NUMBER_OF_CHANNEL @@ -657,7 +657,6 @@ static int __devinit stm_probe(struct platform_device *pdev) /* 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"); -- cgit v1.2.3 From 3813324312bfe1b9ebd78134dfea64a5c03e843d Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Mon, 30 Jan 2012 09:50:34 +0100 Subject: boottime: Port to new 3.3 cpustat interface Signed-off-by: Philippe Langlais --- init/boottime.c | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/init/boottime.c b/init/boottime.c index be73e0ee550..5bdf291a627 100644 --- a/init/boottime.c +++ b/init/boottime.c @@ -44,7 +44,7 @@ struct boottime_list { /* Time in us since power on, possible including boot loader. */ unsigned long time; bool cpu_load; - struct cpu_usage_stat cpu_usage[NR_CPUS]; + struct kernel_cpustat cpu_usage[NR_CPUS]; }; enum boottime_filter_type { @@ -124,10 +124,14 @@ static void __init boottime_mark_core(char *name, 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; + b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] = + kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]; + b->cpu_usage[i].cpustat[CPUTIME_IDLE] = + kcpustat_cpu(i).cpustat[CPUTIME_IDLE]; + b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] = + kcpustat_cpu(i).cpustat[CPUTIME_IOWAIT]; + b->cpu_usage[i].cpustat[CPUTIME_IRQ] = + kcpustat_cpu(i).cpustat[CPUTIME_IRQ]; /* * TODO: Make sure that user, nice, softirq, steal * and guest are not used during boot @@ -253,23 +257,27 @@ static void boottime_debugfs_load(struct seq_file *s, 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_b = (b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] + + b->cpu_usage[i].cpustat[CPUTIME_IDLE] + + b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] + + b->cpu_usage[i].cpustat[CPUTIME_IRQ]); - total_p = (p->cpu_usage[i].system + - p->cpu_usage[i].idle + - p->cpu_usage[i].iowait + - p->cpu_usage[i].irq); + total_p = (p->cpu_usage[i].cpustat[CPUTIME_SYSTEM] + + p->cpu_usage[i].cpustat[CPUTIME_IDLE] + + p->cpu_usage[i].cpustat[CPUTIME_IOWAIT] + + p->cpu_usage[i].cpustat[CPUTIME_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_total = b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] + - p->cpu_usage[i].cpustat[CPUTIME_SYSTEM]; + idle_total = b->cpu_usage[i].cpustat[CPUTIME_IDLE] + - p->cpu_usage[i].cpustat[CPUTIME_IDLE]; + irq_total = b->cpu_usage[i].cpustat[CPUTIME_IRQ] + - p->cpu_usage[i].cpustat[CPUTIME_IRQ]; + iowait_total = b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] + - p->cpu_usage[i].cpustat[CPUTIME_IOWAIT]; system_load = (100 * system_total / (total_b - total_p)); idle_load = (100 * idle_total / (total_b - total_p)); -- cgit v1.2.3 From 489a85202b27f8a2713b01733f6d17b2b4d283bb Mon Sep 17 00:00:00 2001 From: Michel JAOUEN Date: Thu, 12 Jan 2012 17:42:49 +0100 Subject: mach-ux500: hwreg adaptation for u9540 ST-Ericsson ID: 409625 ST-Ericsson FOSS-OUT ID: trivial ST-Ericsson Linux next: NA Depends-On: Iff4121811d2afbf581eec0905077c58bff96ce09 Change-Id: I121bd0f9732342ae5e6753e840473a618e1f3d9f Signed-off-by: Michel JAOUEN Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/45316 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Jonas ABERG --- arch/arm/mach-ux500/hwreg.c | 119 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 102 insertions(+), 17 deletions(-) diff --git a/arch/arm/mach-ux500/hwreg.c b/arch/arm/mach-ux500/hwreg.c index f35472e3ebf..585f2a2b7be 100644 --- a/arch/arm/mach-ux500/hwreg.c +++ b/arch/arm/mach-ux500/hwreg.c @@ -1,4 +1,4 @@ -%/* +/* * Copyright (C) 2011 ST-Ericsson SA * * Author: Etienne CARRIERE for ST-Ericsson @@ -156,10 +156,12 @@ struct hwreg_io_range { u8 *addr; }; +static struct hwreg_io_range *hwreg_io_current_map; + /* * HWREG guts: mapping table */ -static struct hwreg_io_range hwreg_io_map[] = { +static struct hwreg_io_range hwreg_u8500_io_map[] = { /* Periph1 Peripherals */ {.base = U8500_PER1_BASE, .size = 0x10000}, /* Periph2 Peripherals */ @@ -237,17 +239,89 @@ static struct hwreg_io_range hwreg_io_map[] = { }; +static struct hwreg_io_range hwreg_u9540_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 = U9540_ASIC_ID_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) + for (i = 0; hwreg_io_current_map[i].base; ++i) { + hwreg_io_current_map[i].addr = + ioremap(hwreg_io_current_map[i].base, + hwreg_io_current_map[i].size); + if (!hwreg_io_current_map[i].addr) printk(KERN_WARNING "%s: ioremap for %d (%08x) failed\n", - __func__, i, hwreg_io_map[i].base); + __func__, i, hwreg_io_current_map[i].base); } } @@ -255,19 +329,19 @@ 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); + for (i = 0; hwreg_io_current_map[i].base; ++i) + if (hwreg_io_current_map[i].addr) + iounmap(hwreg_io_current_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; + for (i = 0; hwreg_io_current_map[i].base; ++i) { + u32 base = hwreg_io_current_map[i].base; + u32 size = hwreg_io_current_map[i].size; + u8 *addr = hwreg_io_current_map[i].addr; if (phys < base || phys >= base + size) continue; @@ -369,10 +443,12 @@ static const struct file_operations hwreg_value_fops = { static int hwreg_map_print(struct seq_file *s, void *p) { int err, i; - for (i = 0; hwreg_io_map[i].base; ++i) { + + for (i = 0; hwreg_io_current_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); + i, hwreg_io_current_map[i].base, + hwreg_io_current_map[i].base + + hwreg_io_current_map[i].size); if (err < 0) return -ENOMEM; } @@ -602,6 +678,15 @@ static const struct file_operations hwreg_fops = { static int __init hwreg_initialize(void) { static struct dentry *file; + + if (cpu_is_u9540()) { + printk(KERN_INFO "hwreg: cpu is U9540\n"); + hwreg_io_current_map = hwreg_u9540_io_map; + } else { + printk(KERN_INFO "hwreg: cpu is U8500\n"); + hwreg_io_current_map = hwreg_u8500_io_map; + } + hwreg_io_init(); hwreg_debugfs_dir = debugfs_create_dir("mem", NULL); -- cgit v1.2.3 From 0e76cb07853419546152e353b79b24807c6d5663 Mon Sep 17 00:00:00 2001 From: Yann Gautier Date: Tue, 10 Jan 2012 09:36:14 +0100 Subject: stm: mach-ux500: Add support for 9540 ST-Ericsson ID: 398896 Signed-off-by: Yann Gautier --- 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 d38595b11b2..5a634745bad 100644 --- a/arch/arm/mach-ux500/board-mop500-stm.c +++ b/arch/arm/mach-ux500/board-mop500-stm.c @@ -203,7 +203,7 @@ static void control_level_shifter_for_microsd(int gpio_dir) { int gpio[2]; - if (machine_is_hrefv60()) { + if (machine_is_hrefv60() || machine_is_u9540()) { gpio[0] = HREFV60_SDMMC_EN_GPIO; gpio[1] = HREFV60_SDMMC_1V8_3V_GPIO; } else { -- cgit v1.2.3 From eacc602b9c20b99406705804c4e216f18403f7d7 Mon Sep 17 00:00:00 2001 From: Bengt Jonsson Date: Thu, 2 Feb 2012 11:36:49 +0100 Subject: stm: ux500: Add U8520 machine ST-Ericsson ID: 371953 Signed-off-by: Bengt Jonsson --- 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 5a634745bad..34e5892d18b 100644 --- a/arch/arm/mach-ux500/board-mop500-stm.c +++ b/arch/arm/mach-ux500/board-mop500-stm.c @@ -203,7 +203,7 @@ static void control_level_shifter_for_microsd(int gpio_dir) { int gpio[2]; - if (machine_is_hrefv60() || machine_is_u9540()) { + if (machine_is_hrefv60() || machine_is_u8520() || machine_is_u9540()) { gpio[0] = HREFV60_SDMMC_EN_GPIO; gpio[1] = HREFV60_SDMMC_1V8_3V_GPIO; } else { -- cgit v1.2.3 From 647bf84aaa59424e0b3ca9b8c181c7daed11bf2c Mon Sep 17 00:00:00 2001 From: Johan Rudholm Date: Thu, 2 Feb 2012 14:54:37 +0100 Subject: stm: mach-ux500: Update U8520 SD-card GPIO pins ST-Ericsson ID: 371953 Signed-off-by: Bengt Jonsson --- arch/arm/mach-ux500/board-mop500-stm.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-ux500/board-mop500-stm.c b/arch/arm/mach-ux500/board-mop500-stm.c index 34e5892d18b..1bef2a01873 100644 --- a/arch/arm/mach-ux500/board-mop500-stm.c +++ b/arch/arm/mach-ux500/board-mop500-stm.c @@ -26,6 +26,9 @@ #define HREFV60_SDMMC_EN_GPIO 169 #define HREFV60_SDMMC_1V8_3V_GPIO 5 +#define U8520_SDMMC_EN_GPIO 78 +#define U8520_SDMMC_1V8_3V_GPIO 5 + #define STM_DEVICE (&u8500_stm_device.dev) #define STM_ERR(msg) dev_err(STM_DEVICE, msg) #define STM_WARN(msg) dev_warn(STM_DEVICE, msg) @@ -203,9 +206,12 @@ static void control_level_shifter_for_microsd(int gpio_dir) { int gpio[2]; - if (machine_is_hrefv60() || machine_is_u8520() || machine_is_u9540()) { + if (machine_is_hrefv60() || machine_is_u9540()) { gpio[0] = HREFV60_SDMMC_EN_GPIO; gpio[1] = HREFV60_SDMMC_1V8_3V_GPIO; + } else if (machine_is_u8520()) { + gpio[0] = U8520_SDMMC_EN_GPIO; + gpio[1] = U8520_SDMMC_1V8_3V_GPIO; } else { gpio[0] = MOP500_EGPIO(17); gpio[1] = MOP500_EGPIO(18); -- cgit v1.2.3 From 3ce59d85b792eab5a9d7e3e9decc38a014d5c624 Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Wed, 29 Feb 2012 14:08:25 +0100 Subject: init:boottime: Increase static entries Increase the number of static entries from 16 to 32 to be enough room for all measure points taken before kmalloc can be used. ST-Ericsson Linux next: - ST-Ericsson ID: 370799 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ie4326169e56070de4c8b749d3b4e531269983553 Signed-off-by: Jonas Aaberg Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/50937 Reviewed-by: QABUILD Reviewed-by: Mattias WALLIN --- init/boottime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/init/boottime.c b/init/boottime.c index 5bdf291a627..ff0a8f0381b 100644 --- a/init/boottime.c +++ b/init/boottime.c @@ -36,7 +36,7 @@ * 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 +#define NUM_STATIC_BOOTTIME_ENTRIES 32 struct boottime_list { struct list_head list; -- cgit v1.2.3