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 --- drivers/misc/Kconfig.stm | 114 +++++++ drivers/misc/stm.c | 797 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 911 insertions(+) create mode 100644 drivers/misc/Kconfig.stm create mode 100644 drivers/misc/stm.c (limited to 'drivers') diff --git a/drivers/misc/Kconfig.stm b/drivers/misc/Kconfig.stm new file mode 100644 index 00000000000..ef2b94683a3 --- /dev/null +++ b/drivers/misc/Kconfig.stm @@ -0,0 +1,114 @@ +menuconfig STM_TRACE + bool "STM MIPI Trace driver" + depends on ARCH_U8500 + help + Simple System Trace Module driver. It allows to use and configure the + STM, either from kernel space, or from user space. + +if STM_TRACE + +config STM_NUMBER_OF_CHANNEL + int + default 512 if ARCH_U8500 + default 256 + help + Number Max of channels always a multiple of 256 + +config STM_PRINTK + bool "printk support" + depends on STM_TRACE + help + Duplicate printk output on STM printk channel & activate stm_printk + +config STM_PRINTK_CHANNEL + int "printk channel" + range 0 255 + depends on STM_PRINTK + default 255 + help + STM printk channel number + +config STM_FTRACE + bool "functions tracing" + depends on FTRACE + default y + help + Output function tracing on STM dedicated channel + +config STM_FTRACE_CHANNEL + int "ftrace channel" + range 0 255 + depends on STM_FTRACE + default 254 + help + STM ftrace channel number + +config STM_CTX_SWITCH + bool "Context switch tracing" + depends on CONTEXT_SWITCH_TRACER + default y + help + Output scheduler context switch on STM dedicated channel + +config STM_CTX_SWITCH_CHANNEL + int "Context switch channel" + range 0 255 + depends on STM_CTX_SWITCH + default 253 + help + STM Context switch channel number + +config STM_WAKEUP + bool "Scheduler wakeup tracing" + depends on CONTEXT_SWITCH_TRACER + default y + help + Output scheduler wakeup on STM dedicated channel + +config STM_WAKEUP_CHANNEL + int "Wakeup channel" + range 0 255 + depends on STM_WAKEUP + default 252 + help + STM scheduler wakeup channel number + +config STM_STACK_TRACE + bool "Stack tracing" + depends on STACKTRACE + default y + help + Output stack tracing on STM dedicated channel + +config STM_STACK_TRACE_CHANNEL + int "Stack trace channel" + range 0 255 + depends on STM_STACK_TRACE + default 251 + help + STM stack trace channel number + +config STM_TRACE_PRINTK + bool "trace printk & binary printk support" + depends on TRACING + default y + help + Duplicate trace printk output on STM printk channel + +config STM_TRACE_PRINTK_CHANNEL + int "trace_printk channel" + range 0 255 + depends on TRACING + default 250 + help + STM trace_printk channel number + +config STM_TRACE_BPRINTK_CHANNEL + int "trace_bprintk channel" + range 0 255 + depends on TRACING + default 249 + help + STM trace binary printk channel number + +endif diff --git a/drivers/misc/stm.c b/drivers/misc/stm.c new file mode 100644 index 00000000000..a05b2d14573 --- /dev/null +++ b/drivers/misc/stm.c @@ -0,0 +1,797 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Pierre Peiffer 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"); -- 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(+) (limited to 'drivers') 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 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(-) (limited to 'drivers') 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 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(-) (limited to 'drivers') 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