blob: 6979bf04b6f782416c7b3349ed93b9b479d8d55b [file] [log] [blame]
Bhargav Gurappadi0084bbd2016-08-04 14:44:23 -07001/*
Lingutla Chandrasekhar1e4616782018-08-13 17:07:14 +05302 * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
Bhargav Gurappadi0084bbd2016-08-04 14:44:23 -07003 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/atomic.h>
15#include <linux/export.h>
16#include <linux/kernel.h>
17#include <linux/module.h>
18#include <linux/dma-mapping.h>
19#include <linux/mod_devicetable.h>
20#include <linux/platform_device.h>
21#include <linux/sched.h>
22#include <linux/slab.h>
23#include <linux/string.h>
24#include <linux/atomic.h>
25#include <linux/of.h>
26#include <linux/of_address.h>
27#include <linux/io.h>
28#include <asm-generic/sizes.h>
29#include <linux/msm_rtb.h>
Srinivas Ramana55386492016-12-08 15:51:04 +053030#include <asm/timex.h>
Lingutla Chandrasekharbe48b072017-09-25 19:07:12 +053031#include <soc/qcom/minidump.h>
Bhargav Gurappadi0084bbd2016-08-04 14:44:23 -070032
33#define SENTINEL_BYTE_1 0xFF
34#define SENTINEL_BYTE_2 0xAA
35#define SENTINEL_BYTE_3 0xFF
36
37#define RTB_COMPAT_STR "qcom,msm-rtb"
38
39/* Write
40 * 1) 3 bytes sentinel
41 * 2) 1 bytes of log type
42 * 3) 8 bytes of where the caller came from
43 * 4) 4 bytes index
44 * 4) 8 bytes extra data from the caller
45 * 5) 8 bytes of timestamp
Srinivas Ramana55386492016-12-08 15:51:04 +053046 * 6) 8 bytes of cyclecount
Bhargav Gurappadi0084bbd2016-08-04 14:44:23 -070047 *
Srinivas Ramana55386492016-12-08 15:51:04 +053048 * Total = 40 bytes.
Bhargav Gurappadi0084bbd2016-08-04 14:44:23 -070049 */
50struct msm_rtb_layout {
51 unsigned char sentinel[3];
52 unsigned char log_type;
53 uint32_t idx;
54 uint64_t caller;
55 uint64_t data;
56 uint64_t timestamp;
Srinivas Ramana55386492016-12-08 15:51:04 +053057 uint64_t cycle_count;
Bhargav Gurappadi0084bbd2016-08-04 14:44:23 -070058} __attribute__ ((__packed__));
59
60
61struct msm_rtb_state {
62 struct msm_rtb_layout *rtb;
63 phys_addr_t phys;
64 int nentries;
65 int size;
66 int enabled;
67 int initialized;
68 uint32_t filter;
69 int step_size;
70};
71
72#if defined(CONFIG_QCOM_RTB_SEPARATE_CPUS)
73DEFINE_PER_CPU(atomic_t, msm_rtb_idx_cpu);
74#else
75static atomic_t msm_rtb_idx;
76#endif
77
78static struct msm_rtb_state msm_rtb = {
79 .filter = 1 << LOGK_LOGBUF,
80 .enabled = 1,
81};
82
83module_param_named(filter, msm_rtb.filter, uint, 0644);
84module_param_named(enable, msm_rtb.enabled, int, 0644);
85
86static int msm_rtb_panic_notifier(struct notifier_block *this,
87 unsigned long event, void *ptr)
88{
89 msm_rtb.enabled = 0;
90 return NOTIFY_DONE;
91}
92
93static struct notifier_block msm_rtb_panic_blk = {
94 .notifier_call = msm_rtb_panic_notifier,
95 .priority = INT_MAX,
96};
97
98int notrace msm_rtb_event_should_log(enum logk_event_type log_type)
99{
100 return msm_rtb.initialized && msm_rtb.enabled &&
101 ((1 << (log_type & ~LOGTYPE_NOPC)) & msm_rtb.filter);
102}
103EXPORT_SYMBOL(msm_rtb_event_should_log);
104
105static void msm_rtb_emit_sentinel(struct msm_rtb_layout *start)
106{
107 start->sentinel[0] = SENTINEL_BYTE_1;
108 start->sentinel[1] = SENTINEL_BYTE_2;
109 start->sentinel[2] = SENTINEL_BYTE_3;
110}
111
112static void msm_rtb_write_type(enum logk_event_type log_type,
113 struct msm_rtb_layout *start)
114{
115 start->log_type = (char)log_type;
116}
117
118static void msm_rtb_write_caller(uint64_t caller, struct msm_rtb_layout *start)
119{
120 start->caller = caller;
121}
122
123static void msm_rtb_write_idx(uint32_t idx,
124 struct msm_rtb_layout *start)
125{
126 start->idx = idx;
127}
128
129static void msm_rtb_write_data(uint64_t data, struct msm_rtb_layout *start)
130{
131 start->data = data;
132}
133
134static void msm_rtb_write_timestamp(struct msm_rtb_layout *start)
135{
136 start->timestamp = sched_clock();
137}
138
Srinivas Ramana55386492016-12-08 15:51:04 +0530139static void msm_rtb_write_cyclecount(struct msm_rtb_layout *start)
140{
141 start->cycle_count = get_cycles();
142}
143
Bhargav Gurappadi0084bbd2016-08-04 14:44:23 -0700144static void uncached_logk_pc_idx(enum logk_event_type log_type, uint64_t caller,
145 uint64_t data, int idx)
146{
147 struct msm_rtb_layout *start;
148
149 start = &msm_rtb.rtb[idx & (msm_rtb.nentries - 1)];
150
151 msm_rtb_emit_sentinel(start);
152 msm_rtb_write_type(log_type, start);
153 msm_rtb_write_caller(caller, start);
154 msm_rtb_write_idx(idx, start);
155 msm_rtb_write_data(data, start);
156 msm_rtb_write_timestamp(start);
Srinivas Ramana55386492016-12-08 15:51:04 +0530157 msm_rtb_write_cyclecount(start);
Bhargav Gurappadi0084bbd2016-08-04 14:44:23 -0700158 mb();
159
160}
161
162static void uncached_logk_timestamp(int idx)
163{
164 unsigned long long timestamp;
165
166 timestamp = sched_clock();
167 uncached_logk_pc_idx(LOGK_TIMESTAMP|LOGTYPE_NOPC,
168 (uint64_t)lower_32_bits(timestamp),
169 (uint64_t)upper_32_bits(timestamp), idx);
170}
171
172#if defined(CONFIG_QCOM_RTB_SEPARATE_CPUS)
173static int msm_rtb_get_idx(void)
174{
175 int cpu, i, offset;
176 atomic_t *index;
177
178 /*
179 * ideally we would use get_cpu but this is a close enough
180 * approximation for our purposes.
181 */
182 cpu = raw_smp_processor_id();
183
184 index = &per_cpu(msm_rtb_idx_cpu, cpu);
185
186 i = atomic_add_return(msm_rtb.step_size, index);
187 i -= msm_rtb.step_size;
188
189 /* Check if index has wrapped around */
190 offset = (i & (msm_rtb.nentries - 1)) -
191 ((i - msm_rtb.step_size) & (msm_rtb.nentries - 1));
192 if (offset < 0) {
193 uncached_logk_timestamp(i);
194 i = atomic_add_return(msm_rtb.step_size, index);
195 i -= msm_rtb.step_size;
196 }
197
198 return i;
199}
200#else
201static int msm_rtb_get_idx(void)
202{
203 int i, offset;
204
205 i = atomic_inc_return(&msm_rtb_idx);
206 i--;
207
208 /* Check if index has wrapped around */
209 offset = (i & (msm_rtb.nentries - 1)) -
210 ((i - 1) & (msm_rtb.nentries - 1));
211 if (offset < 0) {
212 uncached_logk_timestamp(i);
213 i = atomic_inc_return(&msm_rtb_idx);
214 i--;
215 }
216
217 return i;
218}
219#endif
220
221int notrace uncached_logk_pc(enum logk_event_type log_type, void *caller,
222 void *data)
223{
224 int i;
225
226 if (!msm_rtb_event_should_log(log_type))
227 return 0;
228
229 i = msm_rtb_get_idx();
230 uncached_logk_pc_idx(log_type, (uint64_t)((unsigned long) caller),
231 (uint64_t)((unsigned long) data), i);
232
233 return 1;
234}
235EXPORT_SYMBOL(uncached_logk_pc);
236
237noinline int notrace uncached_logk(enum logk_event_type log_type, void *data)
238{
239 return uncached_logk_pc(log_type, __builtin_return_address(0), data);
240}
241EXPORT_SYMBOL(uncached_logk);
242
243static int msm_rtb_probe(struct platform_device *pdev)
244{
245 struct msm_rtb_platform_data *d = pdev->dev.platform_data;
Lingutla Chandrasekharbe48b072017-09-25 19:07:12 +0530246 struct md_region md_entry;
Bhargav Gurappadi0084bbd2016-08-04 14:44:23 -0700247#if defined(CONFIG_QCOM_RTB_SEPARATE_CPUS)
248 unsigned int cpu;
249#endif
250 int ret;
251
252 if (!pdev->dev.of_node) {
253 msm_rtb.size = d->size;
254 } else {
255 u64 size;
256 struct device_node *pnode;
257
258 pnode = of_parse_phandle(pdev->dev.of_node,
259 "linux,contiguous-region", 0);
260 if (pnode != NULL) {
261 const u32 *addr;
262
263 addr = of_get_address(pnode, 0, &size, NULL);
264 if (!addr) {
265 of_node_put(pnode);
266 return -EINVAL;
267 }
268 of_node_put(pnode);
269 } else {
270 ret = of_property_read_u32(pdev->dev.of_node,
271 "qcom,rtb-size",
272 (u32 *)&size);
273 if (ret < 0)
274 return ret;
275
276 }
277
278 msm_rtb.size = size;
279 }
280
281 if (msm_rtb.size <= 0 || msm_rtb.size > SZ_1M)
282 return -EINVAL;
283
284 msm_rtb.rtb = dma_alloc_coherent(&pdev->dev, msm_rtb.size,
285 &msm_rtb.phys,
286 GFP_KERNEL);
287
288 if (!msm_rtb.rtb)
289 return -ENOMEM;
290
291 msm_rtb.nentries = msm_rtb.size / sizeof(struct msm_rtb_layout);
292
293 /* Round this down to a power of 2 */
294 msm_rtb.nentries = __rounddown_pow_of_two(msm_rtb.nentries);
295
296 memset(msm_rtb.rtb, 0, msm_rtb.size);
297
Lingutla Chandrasekharbe48b072017-09-25 19:07:12 +0530298 strlcpy(md_entry.name, "KRTB_BUF", sizeof(md_entry.name));
299 md_entry.virt_addr = (uintptr_t)msm_rtb.rtb;
300 md_entry.phys_addr = msm_rtb.phys;
301 md_entry.size = msm_rtb.size;
Lingutla Chandrasekhar1e4616782018-08-13 17:07:14 +0530302 if (msm_minidump_add_region(&md_entry) < 0)
Lingutla Chandrasekharbe48b072017-09-25 19:07:12 +0530303 pr_info("Failed to add RTB in Minidump\n");
Bhargav Gurappadi0084bbd2016-08-04 14:44:23 -0700304
305#if defined(CONFIG_QCOM_RTB_SEPARATE_CPUS)
306 for_each_possible_cpu(cpu) {
307 atomic_t *a = &per_cpu(msm_rtb_idx_cpu, cpu);
308
309 atomic_set(a, cpu);
310 }
311 msm_rtb.step_size = num_possible_cpus();
312#else
313 atomic_set(&msm_rtb_idx, 0);
314 msm_rtb.step_size = 1;
315#endif
316
317 atomic_notifier_chain_register(&panic_notifier_list,
318 &msm_rtb_panic_blk);
319 msm_rtb.initialized = 1;
320 return 0;
321}
322
323static const struct of_device_id msm_match_table[] = {
324 {.compatible = RTB_COMPAT_STR},
325 {},
326};
327
328static struct platform_driver msm_rtb_driver = {
329 .driver = {
330 .name = "msm_rtb",
331 .owner = THIS_MODULE,
332 .of_match_table = msm_match_table
333 },
334};
335
336static int __init msm_rtb_init(void)
337{
338 return platform_driver_probe(&msm_rtb_driver, msm_rtb_probe);
339}
340
341static void __exit msm_rtb_exit(void)
342{
343 platform_driver_unregister(&msm_rtb_driver);
344}
345module_init(msm_rtb_init)
346module_exit(msm_rtb_exit)