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