Commit | Line | Data |
---|---|---|
dd101ca5 DC |
1 | /* |
2 | * Copyright (c) 2013 Samsung Electronics Co., Ltd. | |
3 | * http://www.samsung.com | |
4 | * | |
5 | * Exynos-SnapShot debugging framework for Exynos SoC | |
6 | * | |
7 | * Author: Hosung Kim <Hosung0.kim@samsung.com> | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License version 2 as | |
11 | * published by the Free Software Foundation. | |
12 | */ | |
13 | ||
14 | #include <linux/kernel.h> | |
15 | #include <linux/slab.h> | |
16 | #include <linux/module.h> | |
17 | #include <linux/ktime.h> | |
18 | #include <linux/kallsyms.h> | |
19 | #include <linux/platform_device.h> | |
20 | #include <linux/clk-provider.h> | |
21 | #include <linux/pstore_ram.h> | |
22 | #include <linux/sched/clock.h> | |
23 | #include <linux/ftrace.h> | |
24 | ||
25 | #include "debug-snapshot-local.h" | |
26 | #include <asm/irq.h> | |
27 | #include <asm/traps.h> | |
28 | #include <asm/hardirq.h> | |
29 | #include <asm/stacktrace.h> | |
30 | #include <linux/debug-snapshot.h> | |
31 | #include <linux/kernel_stat.h> | |
32 | #include <linux/irqnr.h> | |
33 | #include <linux/irq.h> | |
34 | #include <linux/irqdesc.h> | |
35 | ||
36 | /* | |
37 | * sysfs implementation for debug-snapshot | |
38 | * you can access the sysfs of debug-snapshot to /sys/devices/system/debug_snapshot | |
39 | * path. | |
40 | */ | |
41 | static struct bus_type dss_subsys = { | |
42 | .name = "debug-snapshot", | |
43 | .dev_name = "debug-snapshot", | |
44 | }; | |
45 | ||
46 | extern int dss_irqlog_exlist[DSS_EX_MAX_NUM]; | |
47 | extern int dss_irqexit_exlist[DSS_EX_MAX_NUM]; | |
48 | extern unsigned int dss_irqexit_threshold; | |
49 | ||
16912855 YN |
50 | static ssize_t dss_enable_show(struct device *dev, |
51 | struct device_attribute *attr, char *buf) | |
dd101ca5 DC |
52 | { |
53 | struct dbg_snapshot_item *item; | |
54 | unsigned long i; | |
55 | ssize_t n = 0; | |
56 | ||
57 | /* item */ | |
758cb36c | 58 | for (i = 0; i < dss_desc.log_cnt; i++) { |
dd101ca5 DC |
59 | item = &dss_items[i]; |
60 | n += scnprintf(buf + n, 24, "%-12s : %sable\n", | |
61 | item->name, item->entry.enabled ? "en" : "dis"); | |
62 | } | |
63 | ||
64 | /* base */ | |
65 | n += scnprintf(buf + n, 24, "%-12s : %sable\n", | |
66 | "base", dss_base.enabled ? "en" : "dis"); | |
67 | ||
68 | return n; | |
69 | } | |
70 | ||
16912855 YN |
71 | static ssize_t dss_enable_store(struct device *dev, |
72 | struct device_attribute *attr, | |
dd101ca5 DC |
73 | const char *buf, size_t count) |
74 | { | |
75 | int en; | |
76 | char *name; | |
77 | ||
78 | name = (char *)kstrndup(buf, count, GFP_KERNEL); | |
79 | if (!name) | |
80 | return count; | |
81 | ||
82 | name[count - 1] = '\0'; | |
83 | ||
84 | en = dbg_snapshot_get_enable(name); | |
85 | ||
86 | if (en == -1) | |
87 | pr_info("echo name > enabled\n"); | |
88 | else { | |
89 | if (en) | |
90 | dbg_snapshot_set_enable(name, false); | |
91 | else | |
92 | dbg_snapshot_set_enable(name, true); | |
93 | } | |
94 | ||
95 | kfree(name); | |
96 | return count; | |
97 | } | |
98 | ||
16912855 YN |
99 | static ssize_t dss_callstack_show(struct device *dev, |
100 | struct device_attribute *attr, char *buf) | |
dd101ca5 DC |
101 | { |
102 | ssize_t n = 0; | |
103 | ||
104 | n = scnprintf(buf, 24, "callstack depth : %d\n", dss_desc.callstack); | |
105 | ||
106 | return n; | |
107 | } | |
108 | ||
16912855 | 109 | static ssize_t dss_callstack_store(struct device *dev, struct device_attribute *attr, |
dd101ca5 DC |
110 | const char *buf, size_t count) |
111 | { | |
112 | unsigned long callstack; | |
113 | ||
114 | callstack = simple_strtoul(buf, NULL, 0); | |
115 | pr_info("callstack depth(min 1, max 4) : %lu\n", callstack); | |
116 | ||
117 | if (callstack < 5 && callstack > 0) { | |
118 | dss_desc.callstack = (unsigned int)callstack; | |
119 | pr_info("success inserting %lu to callstack value\n", callstack); | |
120 | } | |
121 | return count; | |
122 | } | |
123 | ||
16912855 YN |
124 | static ssize_t dss_irqlog_exlist_show(struct device *dev, |
125 | struct device_attribute *attr, char *buf) | |
dd101ca5 DC |
126 | { |
127 | unsigned long i; | |
128 | ssize_t n = 0; | |
129 | ||
130 | n = scnprintf(buf, 24, "excluded irq number\n"); | |
131 | ||
132 | for (i = 0; i < ARRAY_SIZE(dss_irqlog_exlist); i++) { | |
133 | if (dss_irqlog_exlist[i] == 0) | |
134 | break; | |
135 | n += scnprintf(buf + n, 24, "irq num: %-4d\n", dss_irqlog_exlist[i]); | |
136 | } | |
137 | return n; | |
138 | } | |
139 | ||
16912855 YN |
140 | static ssize_t dss_irqlog_exlist_store(struct device *dev, |
141 | struct device_attribute *attr, | |
dd101ca5 DC |
142 | const char *buf, size_t count) |
143 | { | |
144 | unsigned long i; | |
145 | unsigned long irq; | |
146 | ||
147 | irq = simple_strtoul(buf, NULL, 0); | |
148 | pr_info("irq number : %lu\n", irq); | |
149 | ||
150 | for (i = 0; i < ARRAY_SIZE(dss_irqlog_exlist); i++) { | |
151 | if (dss_irqlog_exlist[i] == 0) | |
152 | break; | |
153 | } | |
154 | ||
155 | if (i == ARRAY_SIZE(dss_irqlog_exlist)) { | |
156 | pr_err("list is full\n"); | |
157 | return count; | |
158 | } | |
159 | ||
160 | if (irq != 0) { | |
161 | dss_irqlog_exlist[i] = irq; | |
162 | pr_info("success inserting %lu to list\n", irq); | |
163 | } | |
164 | return count; | |
165 | } | |
166 | ||
167 | #ifdef CONFIG_DEBUG_SNAPSHOT_IRQ_EXIT | |
16912855 YN |
168 | static ssize_t dss_irqexit_exlist_show(struct device *dev, |
169 | struct device_attribute *attr, char *buf) | |
dd101ca5 DC |
170 | { |
171 | unsigned long i; | |
172 | ssize_t n = 0; | |
173 | ||
174 | n = scnprintf(buf, 36, "Excluded irq number\n"); | |
175 | for (i = 0; i < ARRAY_SIZE(dss_irqexit_exlist); i++) { | |
176 | if (dss_irqexit_exlist[i] == 0) | |
177 | break; | |
178 | n += scnprintf(buf + n, 24, "IRQ num: %-4d\n", dss_irqexit_exlist[i]); | |
179 | } | |
180 | return n; | |
181 | } | |
182 | ||
16912855 YN |
183 | static ssize_t dss_irqexit_exlist_store(struct device *dev, |
184 | struct device_attribute *attr, | |
dd101ca5 DC |
185 | const char *buf, size_t count) |
186 | { | |
187 | unsigned long i; | |
188 | unsigned long irq; | |
189 | ||
190 | irq = simple_strtoul(buf, NULL, 0); | |
191 | pr_info("irq number : %lu\n", irq); | |
192 | ||
193 | for (i = 0; i < ARRAY_SIZE(dss_irqexit_exlist); i++) { | |
194 | if (dss_irqexit_exlist[i] == 0) | |
195 | break; | |
196 | } | |
197 | ||
198 | if (i == ARRAY_SIZE(dss_irqexit_exlist)) { | |
199 | pr_err("list is full\n"); | |
200 | return count; | |
201 | } | |
202 | ||
203 | if (irq != 0) { | |
204 | dss_irqexit_exlist[i] = irq; | |
205 | pr_info("success inserting %lu to list\n", irq); | |
206 | } | |
207 | return count; | |
208 | } | |
209 | ||
16912855 YN |
210 | static ssize_t dss_irqexit_threshold_show(struct device *dev, |
211 | struct device_attribute *attr, char *buf) | |
dd101ca5 DC |
212 | { |
213 | ssize_t n; | |
214 | ||
215 | n = scnprintf(buf, 46, "threshold : %12u us\n", dss_irqexit_threshold); | |
216 | return n; | |
217 | } | |
218 | ||
16912855 YN |
219 | static ssize_t dss_irqexit_threshold_store(struct device *dev, |
220 | struct device_attribute *attr, | |
dd101ca5 DC |
221 | const char *buf, size_t count) |
222 | { | |
223 | unsigned long val; | |
224 | ||
225 | val = simple_strtoul(buf, NULL, 0); | |
226 | pr_info("threshold value : %lu\n", val); | |
227 | ||
228 | if (val != 0) { | |
229 | dss_irqexit_threshold = (unsigned int)val; | |
230 | pr_info("success %lu to threshold\n", val); | |
231 | } | |
232 | return count; | |
233 | } | |
234 | #endif | |
235 | ||
236 | #ifdef CONFIG_DEBUG_SNAPSHOT_REG | |
16912855 YN |
237 | static ssize_t dss_reg_exlist_show(struct device *dev, |
238 | struct device_attribute *attr, char *buf) | |
dd101ca5 DC |
239 | { |
240 | unsigned long i; | |
241 | ssize_t n = 0; | |
242 | ||
243 | n = scnprintf(buf, 36, "excluded register address\n"); | |
244 | for (i = 0; i < ARRAY_SIZE(dss_reg_exlist); i++) { | |
245 | if (dss_reg_exlist[i].addr == 0) | |
246 | break; | |
247 | n += scnprintf(buf + n, 40, "register addr: %08zx size: %08zx\n", | |
248 | dss_reg_exlist[i].addr, dss_reg_exlist[i].size); | |
249 | } | |
250 | return n; | |
251 | } | |
252 | ||
16912855 YN |
253 | static ssize_t dss_reg_exlist_store(struct device *dev, |
254 | struct device_attribute *attr, | |
dd101ca5 DC |
255 | const char *buf, size_t count) |
256 | { | |
257 | unsigned long i; | |
258 | size_t addr; | |
259 | ||
260 | addr = simple_strtoul(buf, NULL, 0); | |
261 | pr_info("register addr: %zx\n", addr); | |
262 | ||
263 | for (i = 0; i < ARRAY_SIZE(dss_reg_exlist); i++) { | |
264 | if (dss_reg_exlist[i].addr == 0) | |
265 | break; | |
266 | } | |
267 | if (addr != 0) { | |
268 | dss_reg_exlist[i].size = SZ_4K; | |
269 | dss_reg_exlist[i].addr = addr; | |
270 | pr_info("success %zx to threshold\n", (addr)); | |
271 | } | |
272 | return count; | |
273 | } | |
274 | #endif | |
275 | ||
276 | ||
16912855 | 277 | static struct device_attribute dss_enable_attr = |
dd101ca5 DC |
278 | __ATTR(enabled, 0644, dss_enable_show, dss_enable_store); |
279 | ||
16912855 | 280 | static struct device_attribute dss_callstack_attr = |
dd101ca5 DC |
281 | __ATTR(callstack, 0644, dss_callstack_show, dss_callstack_store); |
282 | ||
16912855 | 283 | static struct device_attribute dss_irqlog_attr = |
dd101ca5 DC |
284 | __ATTR(exlist_irqdisabled, 0644, dss_irqlog_exlist_show, |
285 | dss_irqlog_exlist_store); | |
286 | #ifdef CONFIG_DEBUG_SNAPSHOT_IRQ_EXIT | |
16912855 | 287 | static struct device_attribute dss_irqexit_attr = |
dd101ca5 DC |
288 | __ATTR(exlist_irqexit, 0644, dss_irqexit_exlist_show, |
289 | dss_irqexit_exlist_store); | |
290 | ||
16912855 | 291 | static struct device_attribute dss_irqexit_threshold_attr = |
dd101ca5 DC |
292 | __ATTR(threshold_irqexit, 0644, dss_irqexit_threshold_show, |
293 | dss_irqexit_threshold_store); | |
294 | #endif | |
295 | #ifdef CONFIG_DEBUG_SNAPSHOT_REG | |
296 | ||
16912855 | 297 | static struct device_attribute dss_reg_attr = |
dd101ca5 DC |
298 | __ATTR(exlist_reg, 0644, dss_reg_exlist_show, dss_reg_exlist_store); |
299 | #endif | |
300 | ||
301 | static struct attribute *dss_sysfs_attrs[] = { | |
302 | &dss_enable_attr.attr, | |
303 | &dss_callstack_attr.attr, | |
304 | &dss_irqlog_attr.attr, | |
305 | #ifdef CONFIG_DEBUG_SNAPSHOT_IRQ_EXIT | |
306 | &dss_irqexit_attr.attr, | |
307 | &dss_irqexit_threshold_attr.attr, | |
308 | #endif | |
309 | #ifdef CONFIG_DEBUG_SNAPSHOT_REG | |
310 | &dss_reg_attr.attr, | |
311 | #endif | |
312 | NULL, | |
313 | }; | |
314 | ||
315 | static struct attribute_group dss_sysfs_group = { | |
316 | .attrs = dss_sysfs_attrs, | |
317 | }; | |
318 | ||
319 | static const struct attribute_group *dss_sysfs_groups[] = { | |
320 | &dss_sysfs_group, | |
321 | NULL, | |
322 | }; | |
323 | ||
324 | static int __init dbg_snapshot_sysfs_init(void) | |
325 | { | |
326 | int ret = 0; | |
327 | ||
328 | ret = subsys_system_register(&dss_subsys, dss_sysfs_groups); | |
329 | if (ret) | |
330 | pr_err("fail to register debug-snapshop subsys\n"); | |
331 | ||
332 | return ret; | |
333 | } | |
334 | late_initcall(dbg_snapshot_sysfs_init); |