diff options
Diffstat (limited to 'arch/blackfin/kernel/cplbinfo.c')
-rw-r--r-- | arch/blackfin/kernel/cplbinfo.c | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/arch/blackfin/kernel/cplbinfo.c b/arch/blackfin/kernel/cplbinfo.c new file mode 100644 index 000000000000..64d78300dd08 --- /dev/null +++ b/arch/blackfin/kernel/cplbinfo.c | |||
@@ -0,0 +1,177 @@ | |||
1 | /* | ||
2 | * arch/blackfin/kernel/cplbinfo.c - display CPLB status | ||
3 | * | ||
4 | * Copyright 2004-2008 Analog Devices Inc. | ||
5 | * Licensed under the GPL-2 or later. | ||
6 | */ | ||
7 | |||
8 | #include <linux/ctype.h> | ||
9 | #include <linux/module.h> | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/proc_fs.h> | ||
13 | #include <linux/seq_file.h> | ||
14 | #include <linux/uaccess.h> | ||
15 | |||
16 | #include <asm/cplbinit.h> | ||
17 | #include <asm/blackfin.h> | ||
18 | |||
19 | static char const page_strtbl[][3] = { "1K", "4K", "1M", "4M" }; | ||
20 | #define page(flags) (((flags) & 0x30000) >> 16) | ||
21 | #define strpage(flags) page_strtbl[page(flags)] | ||
22 | |||
23 | struct cplbinfo_data { | ||
24 | loff_t pos; | ||
25 | char cplb_type; | ||
26 | u32 mem_control; | ||
27 | struct cplb_entry *tbl; | ||
28 | int switched; | ||
29 | }; | ||
30 | |||
31 | static void cplbinfo_print_header(struct seq_file *m) | ||
32 | { | ||
33 | seq_printf(m, "Index\tAddress\t\tData\tSize\tU/RD\tU/WR\tS/WR\tSwitch\n"); | ||
34 | } | ||
35 | |||
36 | static int cplbinfo_nomore(struct cplbinfo_data *cdata) | ||
37 | { | ||
38 | return cdata->pos >= MAX_CPLBS; | ||
39 | } | ||
40 | |||
41 | static int cplbinfo_show(struct seq_file *m, void *p) | ||
42 | { | ||
43 | struct cplbinfo_data *cdata; | ||
44 | unsigned long data, addr; | ||
45 | loff_t pos; | ||
46 | |||
47 | cdata = p; | ||
48 | pos = cdata->pos; | ||
49 | addr = cdata->tbl[pos].addr; | ||
50 | data = cdata->tbl[pos].data; | ||
51 | |||
52 | seq_printf(m, | ||
53 | "%d\t0x%08lx\t%05lx\t%s\t%c\t%c\t%c\t%c\n", | ||
54 | (int)pos, addr, data, strpage(data), | ||
55 | (data & CPLB_USER_RD) ? 'Y' : 'N', | ||
56 | (data & CPLB_USER_WR) ? 'Y' : 'N', | ||
57 | (data & CPLB_SUPV_WR) ? 'Y' : 'N', | ||
58 | pos < cdata->switched ? 'N' : 'Y'); | ||
59 | |||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | static void cplbinfo_seq_init(struct cplbinfo_data *cdata, unsigned int cpu) | ||
64 | { | ||
65 | if (cdata->cplb_type == 'I') { | ||
66 | cdata->mem_control = bfin_read_IMEM_CONTROL(); | ||
67 | cdata->tbl = icplb_tbl[cpu]; | ||
68 | cdata->switched = first_switched_icplb; | ||
69 | } else { | ||
70 | cdata->mem_control = bfin_read_DMEM_CONTROL(); | ||
71 | cdata->tbl = dcplb_tbl[cpu]; | ||
72 | cdata->switched = first_switched_dcplb; | ||
73 | } | ||
74 | } | ||
75 | |||
76 | static void *cplbinfo_start(struct seq_file *m, loff_t *pos) | ||
77 | { | ||
78 | struct cplbinfo_data *cdata = m->private; | ||
79 | |||
80 | if (!*pos) { | ||
81 | seq_printf(m, "%cCPLBs are %sabled: 0x%x\n", cdata->cplb_type, | ||
82 | (cdata->mem_control & ENDCPLB ? "en" : "dis"), | ||
83 | cdata->mem_control); | ||
84 | cplbinfo_print_header(m); | ||
85 | } else if (cplbinfo_nomore(cdata)) | ||
86 | return NULL; | ||
87 | |||
88 | get_cpu(); | ||
89 | return cdata; | ||
90 | } | ||
91 | |||
92 | static void *cplbinfo_next(struct seq_file *m, void *p, loff_t *pos) | ||
93 | { | ||
94 | struct cplbinfo_data *cdata = p; | ||
95 | cdata->pos = ++(*pos); | ||
96 | if (cplbinfo_nomore(cdata)) | ||
97 | return NULL; | ||
98 | else | ||
99 | return cdata; | ||
100 | } | ||
101 | |||
102 | static void cplbinfo_stop(struct seq_file *m, void *p) | ||
103 | { | ||
104 | put_cpu(); | ||
105 | } | ||
106 | |||
107 | static const struct seq_operations cplbinfo_sops = { | ||
108 | .start = cplbinfo_start, | ||
109 | .next = cplbinfo_next, | ||
110 | .stop = cplbinfo_stop, | ||
111 | .show = cplbinfo_show, | ||
112 | }; | ||
113 | |||
114 | static int cplbinfo_open(struct inode *inode, struct file *file) | ||
115 | { | ||
116 | char buf[256], *path, *p; | ||
117 | unsigned int cpu; | ||
118 | char *s_cpu, *s_cplb; | ||
119 | int ret; | ||
120 | struct seq_file *m; | ||
121 | struct cplbinfo_data *cdata; | ||
122 | |||
123 | path = d_path(&file->f_path, buf, sizeof(buf)); | ||
124 | if (IS_ERR(path)) | ||
125 | return PTR_ERR(path); | ||
126 | s_cpu = strstr(path, "/cpu"); | ||
127 | s_cplb = strrchr(path, '/'); | ||
128 | if (!s_cpu || !s_cplb) | ||
129 | return -EINVAL; | ||
130 | |||
131 | cpu = simple_strtoul(s_cpu + 4, &p, 10); | ||
132 | if (!cpu_online(cpu)) | ||
133 | return -ENODEV; | ||
134 | |||
135 | ret = seq_open_private(file, &cplbinfo_sops, sizeof(*cdata)); | ||
136 | if (ret) | ||
137 | return ret; | ||
138 | m = file->private_data; | ||
139 | cdata = m->private; | ||
140 | |||
141 | cdata->pos = 0; | ||
142 | cdata->cplb_type = toupper(s_cplb[1]); | ||
143 | cplbinfo_seq_init(cdata, cpu); | ||
144 | |||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | static const struct file_operations cplbinfo_fops = { | ||
149 | .open = cplbinfo_open, | ||
150 | .read = seq_read, | ||
151 | .llseek = seq_lseek, | ||
152 | .release = seq_release_private, | ||
153 | }; | ||
154 | |||
155 | static int __init cplbinfo_init(void) | ||
156 | { | ||
157 | struct proc_dir_entry *cplb_dir, *cpu_dir; | ||
158 | char buf[10]; | ||
159 | unsigned int cpu; | ||
160 | |||
161 | cplb_dir = proc_mkdir("cplbinfo", NULL); | ||
162 | if (!cplb_dir) | ||
163 | return -ENOMEM; | ||
164 | |||
165 | for_each_possible_cpu(cpu) { | ||
166 | sprintf(buf, "cpu%i", cpu); | ||
167 | cpu_dir = proc_mkdir(buf, cplb_dir); | ||
168 | if (!cpu_dir) | ||
169 | return -ENOMEM; | ||
170 | |||
171 | proc_create("icplb", S_IRUGO, cpu_dir, &cplbinfo_fops); | ||
172 | proc_create("dcplb", S_IRUGO, cpu_dir, &cplbinfo_fops); | ||
173 | } | ||
174 | |||
175 | return 0; | ||
176 | } | ||
177 | late_initcall(cplbinfo_init); | ||