Blackfin arch: push cache flushing up to dma_memcpy
[linux-2.6] / arch / blackfin / kernel / cplbinfo.c
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 #ifdef CONFIG_MPU
24
25 struct cplbinfo_data {
26         loff_t pos;
27         char cplb_type;
28         u32 mem_control;
29         struct cplb_entry *tbl;
30         int switched;
31 };
32
33 static void cplbinfo_print_header(struct seq_file *m)
34 {
35         seq_printf(m, "Index\tAddress\t\tData\tSize\tU/RD\tU/WR\tS/WR\tSwitch\n");
36 }
37
38 static int cplbinfo_nomore(struct cplbinfo_data *cdata)
39 {
40         return cdata->pos >= MAX_CPLBS;
41 }
42
43 static int cplbinfo_show(struct seq_file *m, void *p)
44 {
45         struct cplbinfo_data *cdata;
46         unsigned long data, addr;
47         loff_t pos;
48
49         cdata = p;
50         pos = cdata->pos;
51         addr = cdata->tbl[pos].addr;
52         data = cdata->tbl[pos].data;
53
54         seq_printf(m,
55                 "%d\t0x%08lx\t%05lx\t%s\t%c\t%c\t%c\t%c\n",
56                 (int)pos, addr, data, strpage(data),
57                 (data & CPLB_USER_RD) ? 'Y' : 'N',
58                 (data & CPLB_USER_WR) ? 'Y' : 'N',
59                 (data & CPLB_SUPV_WR) ? 'Y' : 'N',
60                 pos < cdata->switched ? 'N' : 'Y');
61
62         return 0;
63 }
64
65 static void cplbinfo_seq_init(struct cplbinfo_data *cdata, unsigned int cpu)
66 {
67         if (cdata->cplb_type == 'I') {
68                 cdata->mem_control = bfin_read_IMEM_CONTROL();
69                 cdata->tbl = icplb_tbl[cpu];
70                 cdata->switched = first_switched_icplb;
71         } else {
72                 cdata->mem_control = bfin_read_DMEM_CONTROL();
73                 cdata->tbl = dcplb_tbl[cpu];
74                 cdata->switched = first_switched_dcplb;
75         }
76 }
77
78 #else
79
80 struct cplbinfo_data {
81         loff_t pos;
82         char cplb_type;
83         u32 mem_control;
84         unsigned long *pdt_tables, *pdt_swapcount;
85         unsigned long cplb_addr, cplb_data;
86 };
87
88 extern int page_size_table[];
89
90 static int cplb_find_entry(unsigned long addr_tbl, unsigned long data_tbl,
91                            unsigned long addr_find, unsigned long data_find)
92 {
93         int i;
94
95         for (i = 0; i < 16; ++i) {
96                 unsigned long cplb_addr = bfin_read32(addr_tbl + i * 4);
97                 unsigned long cplb_data = bfin_read32(data_tbl + i * 4);
98                 if (addr_find >= cplb_addr &&
99                     addr_find < cplb_addr + page_size_table[page(cplb_data)] &&
100                     cplb_data == data_find)
101                         return i;
102         }
103
104         return -1;
105 }
106
107 static void cplbinfo_print_header(struct seq_file *m)
108 {
109         seq_printf(m, "Address\t\tData\tSize\tValid\tLocked\tSwapin\tiCount\toCount\n");
110 }
111
112 static int cplbinfo_nomore(struct cplbinfo_data *cdata)
113 {
114         return cdata->pdt_tables[cdata->pos * 2] == 0xffffffff;
115 }
116
117 static int cplbinfo_show(struct seq_file *m, void *p)
118 {
119         struct cplbinfo_data *cdata;
120         unsigned long data, addr;
121         int entry;
122         loff_t pos;
123
124         cdata = p;
125         pos = cdata->pos * 2;
126         addr = cdata->pdt_tables[pos];
127         data = cdata->pdt_tables[pos + 1];
128         entry = cplb_find_entry(cdata->cplb_addr, cdata->cplb_data, addr, data);
129
130         seq_printf(m,
131                 "0x%08lx\t0x%05lx\t%s\t%c\t%c\t%2d\t%ld\t%ld\n",
132                 addr, data, strpage(data),
133                 (data & CPLB_VALID) ? 'Y' : 'N',
134                 (data & CPLB_LOCK) ? 'Y' : 'N', entry,
135                 cdata->pdt_swapcount[pos],
136                 cdata->pdt_swapcount[pos + 1]);
137
138         return 0;
139 }
140
141 static void cplbinfo_seq_init(struct cplbinfo_data *cdata, unsigned int cpu)
142 {
143         if (cdata->cplb_type == 'I') {
144                 cdata->mem_control = bfin_read_IMEM_CONTROL();
145                 cdata->pdt_tables = ipdt_tables[cpu];
146                 cdata->pdt_swapcount = ipdt_swapcount_tables[cpu];
147                 cdata->cplb_addr = ICPLB_ADDR0;
148                 cdata->cplb_data = ICPLB_DATA0;
149         } else {
150                 cdata->mem_control = bfin_read_DMEM_CONTROL();
151                 cdata->pdt_tables = dpdt_tables[cpu];
152                 cdata->pdt_swapcount = dpdt_swapcount_tables[cpu];
153                 cdata->cplb_addr = DCPLB_ADDR0;
154                 cdata->cplb_data = DCPLB_DATA0;
155         }
156 }
157
158 #endif
159
160 static void *cplbinfo_start(struct seq_file *m, loff_t *pos)
161 {
162         struct cplbinfo_data *cdata = m->private;
163
164         if (!*pos) {
165                 seq_printf(m, "%cCPLBs are %sabled: 0x%x\n", cdata->cplb_type,
166                         (cdata->mem_control & ENDCPLB ? "en" : "dis"),
167                         cdata->mem_control);
168                 cplbinfo_print_header(m);
169         } else if (cplbinfo_nomore(cdata))
170                 return NULL;
171
172         get_cpu();
173         return cdata;
174 }
175
176 static void *cplbinfo_next(struct seq_file *m, void *p, loff_t *pos)
177 {
178         struct cplbinfo_data *cdata = p;
179         cdata->pos = ++(*pos);
180         if (cplbinfo_nomore(cdata))
181                 return NULL;
182         else
183                 return cdata;
184 }
185
186 static void cplbinfo_stop(struct seq_file *m, void *p)
187 {
188         put_cpu();
189 }
190
191 static const struct seq_operations cplbinfo_sops = {
192         .start = cplbinfo_start,
193         .next  = cplbinfo_next,
194         .stop  = cplbinfo_stop,
195         .show  = cplbinfo_show,
196 };
197
198 static int cplbinfo_open(struct inode *inode, struct file *file)
199 {
200         char buf[256], *path, *p;
201         unsigned int cpu;
202         char *s_cpu, *s_cplb;
203         int ret;
204         struct seq_file *m;
205         struct cplbinfo_data *cdata;
206
207         path = d_path(&file->f_path, buf, sizeof(buf));
208         if (IS_ERR(path))
209                 return PTR_ERR(path);
210         s_cpu = strstr(path, "/cpu");
211         s_cplb = strrchr(path, '/');
212         if (!s_cpu || !s_cplb)
213                 return -EINVAL;
214
215         cpu = simple_strtoul(s_cpu + 4, &p, 10);
216         if (!cpu_online(cpu))
217                 return -ENODEV;
218
219         ret = seq_open_private(file, &cplbinfo_sops, sizeof(*cdata));
220         if (ret)
221                 return ret;
222         m = file->private_data;
223         cdata = m->private;
224
225         cdata->pos = 0;
226         cdata->cplb_type = toupper(s_cplb[1]);
227         cplbinfo_seq_init(cdata, cpu);
228
229         return 0;
230 }
231
232 static const struct file_operations cplbinfo_fops = {
233         .open    = cplbinfo_open,
234         .read    = seq_read,
235         .llseek  = seq_lseek,
236         .release = seq_release_private,
237 };
238
239 static int __init cplbinfo_init(void)
240 {
241         struct proc_dir_entry *cplb_dir, *cpu_dir;
242         char buf[10];
243         unsigned int cpu;
244
245         cplb_dir = proc_mkdir("cplbinfo", NULL);
246         if (!cplb_dir)
247                 return -ENOMEM;
248
249         for_each_possible_cpu(cpu) {
250                 sprintf(buf, "cpu%i", cpu);
251                 cpu_dir = proc_mkdir(buf, cplb_dir);
252                 if (!cpu_dir)
253                         return -ENOMEM;
254
255                 proc_create("icplb", S_IRUGO, cpu_dir, &cplbinfo_fops);
256                 proc_create("dcplb", S_IRUGO, cpu_dir, &cplbinfo_fops);
257         }
258
259         return 0;
260 }
261 late_initcall(cplbinfo_init);