]>
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 | ||
5ae89ee0 SM |
20 | static char const page_strtbl[][4] = { |
21 | "1K", "4K", "1M", "4M", | |
22 | #ifdef CONFIG_BF60x | |
23 | "16K", "64K", "16M", "64M", | |
24 | #endif | |
25 | }; | |
26 | #define page(flags) (((flags) & 0x70000) >> 16) | |
ff4c02e4 MF |
27 | #define strpage(flags) page_strtbl[page(flags)] |
28 | ||
a024d41b MF |
29 | struct cplbinfo_data { |
30 | loff_t pos; | |
31 | char cplb_type; | |
32 | u32 mem_control; | |
ff4c02e4 MF |
33 | struct cplb_entry *tbl; |
34 | int switched; | |
a024d41b | 35 | }; |
ff4c02e4 | 36 | |
a024d41b MF |
37 | static void cplbinfo_print_header(struct seq_file *m) |
38 | { | |
39 | seq_printf(m, "Index\tAddress\t\tData\tSize\tU/RD\tU/WR\tS/WR\tSwitch\n"); | |
40 | } | |
ff4c02e4 | 41 | |
a024d41b MF |
42 | static int cplbinfo_nomore(struct cplbinfo_data *cdata) |
43 | { | |
44 | return cdata->pos >= MAX_CPLBS; | |
45 | } | |
46 | ||
47 | static int cplbinfo_show(struct seq_file *m, void *p) | |
48 | { | |
49 | struct cplbinfo_data *cdata; | |
50 | unsigned long data, addr; | |
51 | loff_t pos; | |
52 | ||
53 | cdata = p; | |
54 | pos = cdata->pos; | |
55 | addr = cdata->tbl[pos].addr; | |
56 | data = cdata->tbl[pos].data; | |
57 | ||
58 | seq_printf(m, | |
59 | "%d\t0x%08lx\t%05lx\t%s\t%c\t%c\t%c\t%c\n", | |
60 | (int)pos, addr, data, strpage(data), | |
61 | (data & CPLB_USER_RD) ? 'Y' : 'N', | |
62 | (data & CPLB_USER_WR) ? 'Y' : 'N', | |
63 | (data & CPLB_SUPV_WR) ? 'Y' : 'N', | |
64 | pos < cdata->switched ? 'N' : 'Y'); | |
ff4c02e4 | 65 | |
a024d41b MF |
66 | return 0; |
67 | } | |
68 | ||
69 | static void cplbinfo_seq_init(struct cplbinfo_data *cdata, unsigned int cpu) | |
70 | { | |
71 | if (cdata->cplb_type == 'I') { | |
72 | cdata->mem_control = bfin_read_IMEM_CONTROL(); | |
73 | cdata->tbl = icplb_tbl[cpu]; | |
74 | cdata->switched = first_switched_icplb; | |
75 | } else { | |
76 | cdata->mem_control = bfin_read_DMEM_CONTROL(); | |
77 | cdata->tbl = dcplb_tbl[cpu]; | |
78 | cdata->switched = first_switched_dcplb; | |
79 | } | |
ff4c02e4 MF |
80 | } |
81 | ||
a024d41b MF |
82 | static void *cplbinfo_start(struct seq_file *m, loff_t *pos) |
83 | { | |
84 | struct cplbinfo_data *cdata = m->private; | |
85 | ||
86 | if (!*pos) { | |
87 | seq_printf(m, "%cCPLBs are %sabled: 0x%x\n", cdata->cplb_type, | |
88 | (cdata->mem_control & ENDCPLB ? "en" : "dis"), | |
89 | cdata->mem_control); | |
90 | cplbinfo_print_header(m); | |
91 | } else if (cplbinfo_nomore(cdata)) | |
92 | return NULL; | |
93 | ||
94 | get_cpu(); | |
95 | return cdata; | |
ff4c02e4 MF |
96 | } |
97 | ||
a024d41b MF |
98 | static void *cplbinfo_next(struct seq_file *m, void *p, loff_t *pos) |
99 | { | |
100 | struct cplbinfo_data *cdata = p; | |
101 | cdata->pos = ++(*pos); | |
102 | if (cplbinfo_nomore(cdata)) | |
103 | return NULL; | |
104 | else | |
105 | return cdata; | |
106 | } | |
ff4c02e4 | 107 | |
a024d41b | 108 | static void cplbinfo_stop(struct seq_file *m, void *p) |
ff4c02e4 | 109 | { |
a024d41b | 110 | put_cpu(); |
ff4c02e4 MF |
111 | } |
112 | ||
a024d41b MF |
113 | static const struct seq_operations cplbinfo_sops = { |
114 | .start = cplbinfo_start, | |
115 | .next = cplbinfo_next, | |
116 | .stop = cplbinfo_stop, | |
117 | .show = cplbinfo_show, | |
118 | }; | |
119 | ||
48dee093 MF |
120 | #define CPLBINFO_DCPLB_FLAG 0x80000000 |
121 | ||
a024d41b | 122 | static int cplbinfo_open(struct inode *inode, struct file *file) |
ff4c02e4 | 123 | { |
48dee093 | 124 | char cplb_type; |
d9dda78b | 125 | unsigned int cpu = (unsigned long)PDE_DATA(file_inode(file)); |
a024d41b MF |
126 | int ret; |
127 | struct seq_file *m; | |
128 | struct cplbinfo_data *cdata; | |
129 | ||
48dee093 MF |
130 | cplb_type = cpu & CPLBINFO_DCPLB_FLAG ? 'D' : 'I'; |
131 | cpu &= ~CPLBINFO_DCPLB_FLAG; | |
a024d41b | 132 | |
a024d41b MF |
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; | |
48dee093 | 143 | cdata->cplb_type = cplb_type; |
a024d41b MF |
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 | ||
48dee093 MF |
172 | proc_create_data("icplb", S_IRUGO, cpu_dir, &cplbinfo_fops, |
173 | (void *)cpu); | |
174 | proc_create_data("dcplb", S_IRUGO, cpu_dir, &cplbinfo_fops, | |
175 | (void *)(cpu | CPLBINFO_DCPLB_FLAG)); | |
ff4c02e4 MF |
176 | } |
177 | ||
178 | return 0; | |
179 | } | |
180 | late_initcall(cplbinfo_init); |