aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLee Jones <lee.jones@linaro.org>2013-01-14 11:10:36 -0500
committerLee Jones <lee.jones@linaro.org>2013-02-04 03:31:36 -0500
commit4b8ac08256781c59b20bfd86fddcf5d620833752 (patch)
tree38ae77a810412dd288a0b5224841374f5b6f6125
parente0f4fec030ce412666cc127702adbf0a6cfa0855 (diff)
mfd: ab8500-debugfs: Provide a means for a user subscribe to IRQs
Allow users to subscribe to and view IRQ events live from debugfs. Signed-off-by: Lee Jones <lee.jones@linaro.org>
-rw-r--r--drivers/mfd/ab8500-debugfs.c198
1 files changed, 198 insertions, 0 deletions
diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c
index 5a8e707bc038..2e40e12fc687 100644
--- a/drivers/mfd/ab8500-debugfs.c
+++ b/drivers/mfd/ab8500-debugfs.c
@@ -11,6 +11,9 @@
11#include <linux/module.h> 11#include <linux/module.h>
12#include <linux/debugfs.h> 12#include <linux/debugfs.h>
13#include <linux/platform_device.h> 13#include <linux/platform_device.h>
14#include <linux/interrupt.h>
15#include <linux/kobject.h>
16#include <linux/slab.h>
14 17
15#include <linux/mfd/abx500.h> 18#include <linux/mfd/abx500.h>
16#include <linux/mfd/abx500/ab8500.h> 19#include <linux/mfd/abx500/ab8500.h>
@@ -18,6 +21,9 @@
18static u32 debug_bank; 21static u32 debug_bank;
19static u32 debug_address; 22static u32 debug_address;
20 23
24static int irq_first;
25static int irq_last;
26
21/** 27/**
22 * struct ab8500_reg_range 28 * struct ab8500_reg_range
23 * @first: the first address of the range 29 * @first: the first address of the range
@@ -354,6 +360,21 @@ static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
354 }, 360 },
355}; 361};
356 362
363static irqreturn_t ab8500_debug_handler(int irq, void *data)
364{
365 char buf[16];
366 struct kobject *kobj = (struct kobject *)data;
367
368 /*
369 * This makes it possible to use poll for events (POLLPRI | POLLERR)
370 * from userspace on sysfs file named irq-<nr>
371 */
372 sprintf(buf, "irq-%d", irq);
373 sysfs_notify(kobj, NULL, buf);
374
375 return IRQ_HANDLED;
376}
377
357static int ab8500_registers_print(struct seq_file *s, void *p) 378static int ab8500_registers_print(struct seq_file *s, void *p)
358{ 379{
359 struct device *dev = s->private; 380 struct device *dev = s->private;
@@ -519,6 +540,131 @@ static ssize_t ab8500_val_write(struct file *file,
519 return count; 540 return count;
520} 541}
521 542
543static int ab8500_subscribe_unsubscribe_print(struct seq_file *s, void *p)
544{
545 seq_printf(s, "%d\n", irq_first);
546
547 return 0;
548}
549
550static int ab8500_subscribe_unsubscribe_open(struct inode *inode,
551 struct file *file)
552{
553 return single_open(file, ab8500_subscribe_unsubscribe_print,
554 inode->i_private);
555}
556
557/*
558 * This function is used for all interrupts and will always print
559 * the same string. It is however this file sysfs_notify called on.
560 * Userspace should read this file and then poll. When an event occur
561 * the blocking poll will be released.
562 */
563static ssize_t show_irq(struct device *dev,
564 struct device_attribute *attr, char *buf)
565{
566 return sprintf(buf, "irq\n");
567}
568
569static struct device_attribute *dev_attr[AB8500_NR_IRQS];
570static char *event_name[AB8500_NR_IRQS];
571
572static ssize_t ab8500_subscribe_write(struct file *file,
573 const char __user *user_buf,
574 size_t count, loff_t *ppos)
575{
576 struct device *dev = ((struct seq_file *)(file->private_data))->private;
577 char buf[32];
578 int buf_size;
579 unsigned long user_val;
580 int err;
581
582 /* Get userspace string and assure termination */
583 buf_size = min(count, (sizeof(buf)-1));
584 if (copy_from_user(buf, user_buf, buf_size))
585 return -EFAULT;
586 buf[buf_size] = 0;
587
588 err = strict_strtoul(buf, 0, &user_val);
589 if (err)
590 return -EINVAL;
591 if (user_val < irq_first) {
592 dev_err(dev, "debugfs error input < %d\n", irq_first);
593 return -EINVAL;
594 }
595 if (user_val > irq_last) {
596 dev_err(dev, "debugfs error input > %d\n", irq_last);
597 return -EINVAL;
598 }
599
600 /*
601 * This will create a sysfs file named irq-<nr> which userspace can
602 * use to select or poll and get the AB8500 events
603 */
604 dev_attr[user_val] = kmalloc(sizeof(struct device_attribute),
605 GFP_KERNEL);
606 event_name[user_val] = kmalloc(buf_size, GFP_KERNEL);
607 sprintf(event_name[user_val], "irq-%lu", user_val);
608 dev_attr[user_val]->show = show_irq;
609 dev_attr[user_val]->store = NULL;
610 dev_attr[user_val]->attr.name = event_name[user_val];
611 dev_attr[user_val]->attr.mode = S_IRUGO;
612 err = sysfs_create_file(&dev->kobj, &dev_attr[user_val]->attr);
613 if (err < 0) {
614 printk(KERN_ERR "sysfs_create_file failed %d\n", err);
615 return err;
616 }
617
618 err = request_threaded_irq(user_val, NULL, ab8500_debug_handler,
619 IRQF_SHARED | IRQF_NO_SUSPEND,
620 "ab8500-debug", &dev->kobj);
621 if (err < 0) {
622 printk(KERN_ERR "request_threaded_irq failed %d, %lu\n",
623 err, user_val);
624 return err;
625 }
626
627 return buf_size;
628}
629
630static ssize_t ab8500_unsubscribe_write(struct file *file,
631 const char __user *user_buf,
632 size_t count, loff_t *ppos)
633{
634 struct device *dev = ((struct seq_file *)(file->private_data))->private;
635 char buf[32];
636 int buf_size;
637 unsigned long user_val;
638 int err;
639
640 /* Get userspace string and assure termination */
641 buf_size = min(count, (sizeof(buf)-1));
642 if (copy_from_user(buf, user_buf, buf_size))
643 return -EFAULT;
644 buf[buf_size] = 0;
645
646 err = strict_strtoul(buf, 0, &user_val);
647 if (err)
648 return -EINVAL;
649 if (user_val < irq_first) {
650 dev_err(dev, "debugfs error input < %d\n", irq_first);
651 return -EINVAL;
652 }
653 if (user_val > irq_last) {
654 dev_err(dev, "debugfs error input > %d\n", irq_last);
655 return -EINVAL;
656 }
657
658 free_irq(user_val, &dev->kobj);
659 kfree(event_name[user_val]);
660 kfree(dev_attr[user_val]);
661
662 if (dev_attr[user_val])
663 sysfs_remove_file(&dev->kobj, &dev_attr[user_val]->attr);
664
665 return buf_size;
666}
667
522static const struct file_operations ab8500_bank_fops = { 668static const struct file_operations ab8500_bank_fops = {
523 .open = ab8500_bank_open, 669 .open = ab8500_bank_open,
524 .write = ab8500_bank_write, 670 .write = ab8500_bank_write,
@@ -546,17 +692,51 @@ static const struct file_operations ab8500_val_fops = {
546 .owner = THIS_MODULE, 692 .owner = THIS_MODULE,
547}; 693};
548 694
695static const struct file_operations ab8500_subscribe_fops = {
696 .open = ab8500_subscribe_unsubscribe_open,
697 .write = ab8500_subscribe_write,
698 .read = seq_read,
699 .llseek = seq_lseek,
700 .release = single_release,
701 .owner = THIS_MODULE,
702};
703
704static const struct file_operations ab8500_unsubscribe_fops = {
705 .open = ab8500_subscribe_unsubscribe_open,
706 .write = ab8500_unsubscribe_write,
707 .read = seq_read,
708 .llseek = seq_lseek,
709 .release = single_release,
710 .owner = THIS_MODULE,
711};
712
549static struct dentry *ab8500_dir; 713static struct dentry *ab8500_dir;
550static struct dentry *ab8500_reg_file; 714static struct dentry *ab8500_reg_file;
551static struct dentry *ab8500_bank_file; 715static struct dentry *ab8500_bank_file;
552static struct dentry *ab8500_address_file; 716static struct dentry *ab8500_address_file;
553static struct dentry *ab8500_val_file; 717static struct dentry *ab8500_val_file;
718static struct dentry *ab8500_subscribe_file;
719static struct dentry *ab8500_unsubscribe_file;
554 720
555static int ab8500_debug_probe(struct platform_device *plf) 721static int ab8500_debug_probe(struct platform_device *plf)
556{ 722{
557 debug_bank = AB8500_MISC; 723 debug_bank = AB8500_MISC;
558 debug_address = AB8500_REV_REG & 0x00FF; 724 debug_address = AB8500_REV_REG & 0x00FF;
559 725
726 irq_first = platform_get_irq_byname(plf, "IRQ_FIRST");
727 if (irq_first < 0) {
728 dev_err(&plf->dev, "First irq not found, err %d\n",
729 irq_first);
730 return irq_first;
731 }
732
733 irq_last = platform_get_irq_byname(plf, "IRQ_LAST");
734 if (irq_last < 0) {
735 dev_err(&plf->dev, "Last irq not found, err %d\n",
736 irq_last);
737 return irq_last;
738 }
739
560 ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL); 740 ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL);
561 if (!ab8500_dir) 741 if (!ab8500_dir)
562 goto exit_no_debugfs; 742 goto exit_no_debugfs;
@@ -582,8 +762,26 @@ static int ab8500_debug_probe(struct platform_device *plf)
582 if (!ab8500_val_file) 762 if (!ab8500_val_file)
583 goto exit_destroy_address; 763 goto exit_destroy_address;
584 764
765 ab8500_subscribe_file =
766 debugfs_create_file("irq-subscribe",
767 (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev,
768 &ab8500_subscribe_fops);
769 if (!ab8500_subscribe_file)
770 goto exit_destroy_val;
771
772 ab8500_unsubscribe_file =
773 debugfs_create_file("irq-unsubscribe",
774 (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev,
775 &ab8500_unsubscribe_fops);
776 if (!ab8500_unsubscribe_file)
777 goto exit_destroy_subscribe;
778
585 return 0; 779 return 0;
586 780
781exit_destroy_subscribe:
782 debugfs_remove(ab8500_subscribe_file);
783exit_destroy_val:
784 debugfs_remove(ab8500_val_file);
587exit_destroy_address: 785exit_destroy_address:
588 debugfs_remove(ab8500_address_file); 786 debugfs_remove(ab8500_address_file);
589exit_destroy_bank: 787exit_destroy_bank: