aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcarriere etienne <etienne.carriere@stericsson.com>2011-04-08 10:26:36 -0400
committerLee Jones <lee.jones@linaro.org>2013-02-04 03:31:40 -0500
commit0fbce76eff0e7ea92f51b253c504a79d9b3b5769 (patch)
tree048fd646cb12bbb274f15ecf6f130b4c002c989b
parentfad55a869ba1bf0d1ab7e4c8bff0b171eb5486ee (diff)
mfd: ab8500-debugfs: Formated access AB8500 registers from debugfs entry
Add debugfs entry ab8500/hwreg to read/write bit-field in AB8500 registers. Check the debugfs entries usage from heading comments in ab8500-debugfs.c Signed-off-by: Lee Jones <lee.jones@linaro.org> Signed-off-by: carriere etienne <etienne.carriere@stericsson.com> Reviewed-by: Mattias WALLIN <mattias.wallin@stericsson.com>
-rw-r--r--drivers/mfd/ab8500-debugfs.c390
1 files changed, 338 insertions, 52 deletions
diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c
index 1bb74297a3a7..79a954f79732 100644
--- a/drivers/mfd/ab8500-debugfs.c
+++ b/drivers/mfd/ab8500-debugfs.c
@@ -4,6 +4,72 @@
4 * Author: Mattias Wallin <mattias.wallin@stericsson.com> for ST-Ericsson. 4 * Author: Mattias Wallin <mattias.wallin@stericsson.com> for ST-Ericsson.
5 * License Terms: GNU General Public License v2 5 * License Terms: GNU General Public License v2
6 */ 6 */
7/*
8 * AB8500 register access
9 * ======================
10 *
11 * read:
12 * # echo BANK > <debugfs>/ab8500/register-bank
13 * # echo ADDR > <debugfs>/ab8500/register-address
14 * # cat <debugfs>/ab8500/register-value
15 *
16 * write:
17 * # echo BANK > <debugfs>/ab8500/register-bank
18 * # echo ADDR > <debugfs>/ab8500/register-address
19 * # echo VALUE > <debugfs>/ab8500/register-value
20 *
21 * read all registers from a bank:
22 * # echo BANK > <debugfs>/ab8500/register-bank
23 * # cat <debugfs>/ab8500/all-bank-register
24 *
25 * BANK target AB8500 register bank
26 * ADDR target AB8500 register address
27 * VALUE decimal or 0x-prefixed hexadecimal
28 *
29 *
30 * User Space notification on AB8500 IRQ
31 * =====================================
32 *
33 * Allows user space entity to be notified when target AB8500 IRQ occurs.
34 * When subscribed, a sysfs entry is created in ab8500.i2c platform device.
35 * One can pool this file to get target IRQ occurence information.
36 *
37 * subscribe to an AB8500 IRQ:
38 * # echo IRQ > <debugfs>/ab8500/irq-subscribe
39 *
40 * unsubscribe from an AB8500 IRQ:
41 * # echo IRQ > <debugfs>/ab8500/irq-unsubscribe
42 *
43 *
44 * AB8500 register formated read/write access
45 * ==========================================
46 *
47 * Read: read data, data>>SHIFT, data&=MASK, output data
48 * [0xABCDEF98] shift=12 mask=0xFFF => 0x00000CDE
49 * Write: read data, data &= ~(MASK<<SHIFT), data |= (VALUE<<SHIFT), write data
50 * [0xABCDEF98] shift=12 mask=0xFFF value=0x123 => [0xAB123F98]
51 *
52 * Usage:
53 * # echo "CMD [OPTIONS] BANK ADRESS [VALUE]" > $debugfs/ab8500/hwreg
54 *
55 * CMD read read access
56 * write write access
57 *
58 * BANK target reg bank
59 * ADDRESS target reg address
60 * VALUE (write) value to be updated
61 *
62 * OPTIONS
63 * -d|-dec (read) output in decimal
64 * -h|-hexa (read) output in 0x-hexa (default)
65 * -l|-w|-b 32bit (default), 16bit or 8bit reg access
66 * -m|-mask MASK 0x-hexa mask (default 0xFFFFFFFF)
67 * -s|-shift SHIFT bit shift value (read:left, write:right)
68 * -o|-offset OFFSET address offset to add to ADDRESS value
69 *
70 * Warning: bit shift operation is applied to bit-mask.
71 * Warning: bit shift direction depends on read or right command.
72 */
7 73
8#include <linux/seq_file.h> 74#include <linux/seq_file.h>
9#include <linux/uaccess.h> 75#include <linux/uaccess.h>
@@ -18,6 +84,11 @@
18#include <linux/mfd/abx500.h> 84#include <linux/mfd/abx500.h>
19#include <linux/mfd/abx500/ab8500.h> 85#include <linux/mfd/abx500/ab8500.h>
20 86
87#ifdef CONFIG_DEBUG_FS
88#include <linux/string.h>
89#include <linux/ctype.h>
90#endif
91
21static u32 debug_bank; 92static u32 debug_bank;
22static u32 debug_address; 93static u32 debug_address;
23 94
@@ -52,6 +123,25 @@ struct ab8500_prcmu_ranges {
52 const struct ab8500_reg_range *range; 123 const struct ab8500_reg_range *range;
53}; 124};
54 125
126/* hwreg- "mask" and "shift" entries ressources */
127struct hwreg_cfg {
128 u32 bank; /* target bank */
129 u32 addr; /* target address */
130 uint fmt; /* format */
131 uint mask; /* read/write mask, applied before any bit shift */
132 int shift; /* bit shift (read:right shift, write:left shift */
133};
134/* fmt bit #0: 0=hexa, 1=dec */
135#define REG_FMT_DEC(c) ((c)->fmt & 0x1)
136#define REG_FMT_HEX(c) (!REG_FMT_DEC(c))
137
138static struct hwreg_cfg hwreg_cfg = {
139 .addr = 0, /* default: invalid phys addr */
140 .fmt = 0, /* default: 32bit access, hex output */
141 .mask = 0xFFFFFFFF, /* default: no mask */
142 .shift = 0, /* default: no bit shift */
143};
144
55#define AB8500_NAME_STRING "ab8500" 145#define AB8500_NAME_STRING "ab8500"
56#define AB8500_NUM_BANKS 22 146#define AB8500_NUM_BANKS 22
57 147
@@ -547,6 +637,205 @@ static ssize_t ab8500_val_write(struct file *file,
547 return count; 637 return count;
548} 638}
549 639
640/*
641 * - HWREG DB8500 formated routines
642 */
643static int ab8500_hwreg_print(struct seq_file *s, void *d)
644{
645 struct device *dev = s->private;
646 int ret;
647 u8 regvalue;
648
649 ret = abx500_get_register_interruptible(dev,
650 (u8)hwreg_cfg.bank, (u8)hwreg_cfg.addr, &regvalue);
651 if (ret < 0) {
652 dev_err(dev, "abx500_get_reg fail %d, %d\n",
653 ret, __LINE__);
654 return -EINVAL;
655 }
656
657 if (hwreg_cfg.shift >= 0)
658 regvalue >>= hwreg_cfg.shift;
659 else
660 regvalue <<= -hwreg_cfg.shift;
661 regvalue &= hwreg_cfg.mask;
662
663 if (REG_FMT_DEC(&hwreg_cfg))
664 seq_printf(s, "%d\n", regvalue);
665 else
666 seq_printf(s, "0x%02X\n", regvalue);
667 return 0;
668}
669
670static int ab8500_hwreg_open(struct inode *inode, struct file *file)
671{
672 return single_open(file, ab8500_hwreg_print, inode->i_private);
673}
674
675/*
676 * return length of an ASCII numerical value, 0 is string is not a
677 * numerical value.
678 * string shall start at value 1st char.
679 * string can be tailed with \0 or space or newline chars only.
680 * value can be decimal or hexadecimal (prefixed 0x or 0X).
681 */
682static int strval_len(char *b)
683{
684 char *s = b;
685 if ((*s == '0') && ((*(s+1) == 'x') || (*(s+1) == 'X'))) {
686 s += 2;
687 for (; *s && (*s != ' ') && (*s != '\n'); s++) {
688 if (!isxdigit(*s))
689 return 0;
690 }
691 } else {
692 if (*s == '-')
693 s++;
694 for (; *s && (*s != ' ') && (*s != '\n'); s++) {
695 if (!isdigit(*s))
696 return 0;
697 }
698 }
699 return (int) (s-b);
700}
701
702/*
703 * parse hwreg input data.
704 * update global hwreg_cfg only if input data syntax is ok.
705 */
706static ssize_t hwreg_common_write(char *b, struct hwreg_cfg *cfg,
707 struct device *dev)
708{
709 uint write, val = 0;
710 u8 regvalue;
711 int ret;
712 struct hwreg_cfg loc = {
713 .bank = 0, /* default: invalid phys addr */
714 .addr = 0, /* default: invalid phys addr */
715 .fmt = 0, /* default: 32bit access, hex output */
716 .mask = 0xFFFFFFFF, /* default: no mask */
717 .shift = 0, /* default: no bit shift */
718 };
719
720 /* read or write ? */
721 if (!strncmp(b, "read ", 5)) {
722 write = 0;
723 b += 5;
724 } else if (!strncmp(b, "write ", 6)) {
725 write = 1;
726 b += 6;
727 } else
728 return -EINVAL;
729
730 /* OPTIONS -l|-w|-b -s -m -o */
731 while ((*b == ' ') || (*b == '-')) {
732 if (*(b-1) != ' ') {
733 b++;
734 continue;
735 }
736 if ((!strncmp(b, "-d ", 3)) ||
737 (!strncmp(b, "-dec ", 5))) {
738 b += (*(b+2) == ' ') ? 3 : 5;
739 loc.fmt |= (1<<0);
740 } else if ((!strncmp(b, "-h ", 3)) ||
741 (!strncmp(b, "-hex ", 5))) {
742 b += (*(b+2) == ' ') ? 3 : 5;
743 loc.fmt &= ~(1<<0);
744 } else if ((!strncmp(b, "-m ", 3)) ||
745 (!strncmp(b, "-mask ", 6))) {
746 b += (*(b+2) == ' ') ? 3 : 6;
747 if (strval_len(b) == 0)
748 return -EINVAL;
749 loc.mask = simple_strtoul(b, &b, 0);
750 } else if ((!strncmp(b, "-s ", 3)) ||
751 (!strncmp(b, "-shift ", 7))) {
752 b += (*(b+2) == ' ') ? 3 : 7;
753 if (strval_len(b) == 0)
754 return -EINVAL;
755 loc.shift = simple_strtol(b, &b, 0);
756 } else {
757 return -EINVAL;
758 }
759 }
760 /* get arg BANK and ADDRESS */
761 if (strval_len(b) == 0)
762 return -EINVAL;
763 loc.bank = simple_strtoul(b, &b, 0);
764 while (*b == ' ')
765 b++;
766 if (strval_len(b) == 0)
767 return -EINVAL;
768 loc.addr = simple_strtoul(b, &b, 0);
769
770 if (write) {
771 while (*b == ' ')
772 b++;
773 if (strval_len(b) == 0)
774 return -EINVAL;
775 val = simple_strtoul(b, &b, 0);
776 }
777
778 /* args are ok, update target cfg (mainly for read) */
779 *cfg = loc;
780
781#ifdef ABB_HWREG_DEBUG
782 pr_warn("HWREG request: %s, %s, addr=0x%08X, mask=0x%X, shift=%d"
783 "value=0x%X\n", (write) ? "write" : "read",
784 REG_FMT_DEC(cfg) ? "decimal" : "hexa",
785 cfg->addr, cfg->mask, cfg->shift, val);
786#endif
787
788 if (!write)
789 return 0;
790
791 ret = abx500_get_register_interruptible(dev,
792 (u8)cfg->bank, (u8)cfg->addr, &regvalue);
793 if (ret < 0) {
794 dev_err(dev, "abx500_get_reg fail %d, %d\n",
795 ret, __LINE__);
796 return -EINVAL;
797 }
798
799 if (cfg->shift >= 0) {
800 regvalue &= ~(cfg->mask << (cfg->shift));
801 val = (val & cfg->mask) << (cfg->shift);
802 } else {
803 regvalue &= ~(cfg->mask >> (-cfg->shift));
804 val = (val & cfg->mask) >> (-cfg->shift);
805 }
806 val = val | regvalue;
807
808 ret = abx500_set_register_interruptible(dev,
809 (u8)cfg->bank, (u8)cfg->addr, (u8)val);
810 if (ret < 0) {
811 pr_err("abx500_set_reg failed %d, %d", ret, __LINE__);
812 return -EINVAL;
813 }
814
815 return 0;
816}
817
818static ssize_t ab8500_hwreg_write(struct file *file,
819 const char __user *user_buf, size_t count, loff_t *ppos)
820{
821 struct device *dev = ((struct seq_file *)(file->private_data))->private;
822 char buf[128];
823 int buf_size, ret;
824
825 /* Get userspace string and assure termination */
826 buf_size = min(count, (sizeof(buf)-1));
827 if (copy_from_user(buf, user_buf, buf_size))
828 return -EFAULT;
829 buf[buf_size] = 0;
830
831 /* get args and process */
832 ret = hwreg_common_write(buf, &hwreg_cfg, dev);
833 return (ret) ? ret : buf_size;
834}
835
836/*
837 * - irq subscribe/unsubscribe stuff
838 */
550static int ab8500_subscribe_unsubscribe_print(struct seq_file *s, void *p) 839static int ab8500_subscribe_unsubscribe_print(struct seq_file *s, void *p)
551{ 840{
552 seq_printf(s, "%d\n", irq_first); 841 seq_printf(s, "%d\n", irq_first);
@@ -694,6 +983,10 @@ static ssize_t ab8500_unsubscribe_write(struct file *file,
694 return buf_size; 983 return buf_size;
695} 984}
696 985
986/*
987 * - several deubgfs nodes fops
988 */
989
697static const struct file_operations ab8500_bank_fops = { 990static const struct file_operations ab8500_bank_fops = {
698 .open = ab8500_bank_open, 991 .open = ab8500_bank_open,
699 .write = ab8500_bank_write, 992 .write = ab8500_bank_write,
@@ -739,16 +1032,20 @@ static const struct file_operations ab8500_unsubscribe_fops = {
739 .owner = THIS_MODULE, 1032 .owner = THIS_MODULE,
740}; 1033};
741 1034
1035static const struct file_operations ab8500_hwreg_fops = {
1036 .open = ab8500_hwreg_open,
1037 .write = ab8500_hwreg_write,
1038 .read = seq_read,
1039 .llseek = seq_lseek,
1040 .release = single_release,
1041 .owner = THIS_MODULE,
1042};
1043
742static struct dentry *ab8500_dir; 1044static struct dentry *ab8500_dir;
743static struct dentry *ab8500_reg_file;
744static struct dentry *ab8500_bank_file;
745static struct dentry *ab8500_address_file;
746static struct dentry *ab8500_val_file;
747static struct dentry *ab8500_subscribe_file;
748static struct dentry *ab8500_unsubscribe_file;
749 1045
750static int ab8500_debug_probe(struct platform_device *plf) 1046static int ab8500_debug_probe(struct platform_device *plf)
751{ 1047{
1048 struct dentry *file;
752 debug_bank = AB8500_MISC; 1049 debug_bank = AB8500_MISC;
753 debug_address = AB8500_REV_REG & 0x00FF; 1050 debug_address = AB8500_REV_REG & 0x00FF;
754 1051
@@ -768,70 +1065,59 @@ static int ab8500_debug_probe(struct platform_device *plf)
768 1065
769 ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL); 1066 ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL);
770 if (!ab8500_dir) 1067 if (!ab8500_dir)
771 goto exit_no_debugfs; 1068 goto err;
772 1069
773 ab8500_reg_file = debugfs_create_file("all-bank-registers", 1070 file = debugfs_create_file("all-bank-registers",
774 S_IRUGO, ab8500_dir, &plf->dev, &ab8500_registers_fops); 1071 S_IRUGO, ab8500_dir, &plf->dev, &ab8500_registers_fops);
775 if (!ab8500_reg_file) 1072 if (!file)
776 goto exit_destroy_dir; 1073 goto err;
777 1074
778 ab8500_bank_file = debugfs_create_file("register-bank", 1075 file = debugfs_create_file("register-bank",
779 (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_bank_fops); 1076 (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_bank_fops);
780 if (!ab8500_bank_file) 1077 if (!file)
781 goto exit_destroy_reg; 1078 goto err;
782 1079
783 ab8500_address_file = debugfs_create_file("register-address", 1080 file = debugfs_create_file("register-address",
784 (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, 1081 (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev,
785 &ab8500_address_fops); 1082 &ab8500_address_fops);
786 if (!ab8500_address_file) 1083 if (!file)
787 goto exit_destroy_bank; 1084 goto err;
788 1085
789 ab8500_val_file = debugfs_create_file("register-value", 1086 file = debugfs_create_file("register-value",
790 (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_val_fops); 1087 (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_val_fops);
791 if (!ab8500_val_file) 1088 if (!file)
792 goto exit_destroy_address; 1089 goto err;
793 1090
794 ab8500_subscribe_file = 1091 file = debugfs_create_file("irq-subscribe",
795 debugfs_create_file("irq-subscribe", 1092 (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev,
796 (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev, 1093 &ab8500_subscribe_fops);
797 &ab8500_subscribe_fops); 1094 if (!file)
798 if (!ab8500_subscribe_file) 1095 goto err;
799 goto exit_destroy_val; 1096
800 1097 file = debugfs_create_file("irq-unsubscribe",
801 ab8500_unsubscribe_file = 1098 (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev,
802 debugfs_create_file("irq-unsubscribe", 1099 &ab8500_unsubscribe_fops);
803 (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev, 1100 if (!file)
804 &ab8500_unsubscribe_fops); 1101 goto err;
805 if (!ab8500_unsubscribe_file) 1102
806 goto exit_destroy_subscribe; 1103 file = debugfs_create_file("hwreg",
1104 (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev,
1105 &ab8500_hwreg_fops);
1106 if (!file)
1107 goto err;
807 1108
808 return 0; 1109 return 0;
809 1110
810exit_destroy_subscribe: 1111err:
811 debugfs_remove(ab8500_subscribe_file); 1112 if (ab8500_dir)
812exit_destroy_val: 1113 debugfs_remove_recursive(ab8500_dir);
813 debugfs_remove(ab8500_val_file);
814exit_destroy_address:
815 debugfs_remove(ab8500_address_file);
816exit_destroy_bank:
817 debugfs_remove(ab8500_bank_file);
818exit_destroy_reg:
819 debugfs_remove(ab8500_reg_file);
820exit_destroy_dir:
821 debugfs_remove(ab8500_dir);
822exit_no_debugfs:
823 dev_err(&plf->dev, "failed to create debugfs entries.\n"); 1114 dev_err(&plf->dev, "failed to create debugfs entries.\n");
824 return -ENOMEM; 1115 return -ENOMEM;
825} 1116}
826 1117
827static int ab8500_debug_remove(struct platform_device *plf) 1118static int ab8500_debug_remove(struct platform_device *plf)
828{ 1119{
829 debugfs_remove(ab8500_val_file); 1120 debugfs_remove_recursive(ab8500_dir);
830 debugfs_remove(ab8500_address_file);
831 debugfs_remove(ab8500_bank_file);
832 debugfs_remove(ab8500_reg_file);
833 debugfs_remove(ab8500_dir);
834
835 return 0; 1121 return 0;
836} 1122}
837 1123