]>
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 | ||
48dee093 MF |
115 | #define CPLBINFO_DCPLB_FLAG 0x80000000 |
116 | ||
a024d41b | 117 | static int cplbinfo_open(struct inode *inode, struct file *file) |
ff4c02e4 | 118 | { |
496ad9aa | 119 | struct proc_dir_entry *pde = PDE(file_inode(file)); |
48dee093 | 120 | char cplb_type; |
a024d41b | 121 | unsigned int cpu; |
a024d41b MF |
122 | int ret; |
123 | struct seq_file *m; | |
124 | struct cplbinfo_data *cdata; | |
125 | ||
48dee093 MF |
126 | cpu = (unsigned int)pde->data; |
127 | cplb_type = cpu & CPLBINFO_DCPLB_FLAG ? 'D' : 'I'; | |
128 | cpu &= ~CPLBINFO_DCPLB_FLAG; | |
a024d41b | 129 | |
a024d41b MF |
130 | if (!cpu_online(cpu)) |
131 | return -ENODEV; | |
132 | ||
133 | ret = seq_open_private(file, &cplbinfo_sops, sizeof(*cdata)); | |
134 | if (ret) | |
135 | return ret; | |
136 | m = file->private_data; | |
137 | cdata = m->private; | |
138 | ||
139 | cdata->pos = 0; | |
48dee093 | 140 | cdata->cplb_type = cplb_type; |
a024d41b MF |
141 | cplbinfo_seq_init(cdata, cpu); |
142 | ||
143 | return 0; | |
ff4c02e4 MF |
144 | } |
145 | ||
a024d41b MF |
146 | static const struct file_operations cplbinfo_fops = { |
147 | .open = cplbinfo_open, | |
148 | .read = seq_read, | |
149 | .llseek = seq_lseek, | |
150 | .release = seq_release_private, | |
151 | }; | |
152 | ||
ff4c02e4 MF |
153 | static int __init cplbinfo_init(void) |
154 | { | |
a024d41b MF |
155 | struct proc_dir_entry *cplb_dir, *cpu_dir; |
156 | char buf[10]; | |
ff4c02e4 | 157 | unsigned int cpu; |
ff4c02e4 | 158 | |
a024d41b MF |
159 | cplb_dir = proc_mkdir("cplbinfo", NULL); |
160 | if (!cplb_dir) | |
ff4c02e4 MF |
161 | return -ENOMEM; |
162 | ||
a024d41b MF |
163 | for_each_possible_cpu(cpu) { |
164 | sprintf(buf, "cpu%i", cpu); | |
165 | cpu_dir = proc_mkdir(buf, cplb_dir); | |
166 | if (!cpu_dir) | |
ff4c02e4 MF |
167 | return -ENOMEM; |
168 | ||
48dee093 MF |
169 | proc_create_data("icplb", S_IRUGO, cpu_dir, &cplbinfo_fops, |
170 | (void *)cpu); | |
171 | proc_create_data("dcplb", S_IRUGO, cpu_dir, &cplbinfo_fops, | |
172 | (void *)(cpu | CPLBINFO_DCPLB_FLAG)); | |
ff4c02e4 MF |
173 | } |
174 | ||
175 | return 0; | |
176 | } | |
177 | late_initcall(cplbinfo_init); |