blob: 28b21956673c4e088e4d2ab8ba3a0d02563be2bd [file] [log] [blame]
Laura Abbottad340ff2012-01-04 14:23:48 -08001/*
Sarang Joshi31a77242013-08-28 12:57:00 -07002 * Copyright (c) 2013, The Linux Foundation. All rights reserved.
Laura Abbottad340ff2012-01-04 14:23:48 -08003 *
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/memory_alloc.h>
18#include <linux/module.h>
Laura Abbottf7e44042012-06-22 12:50:32 -070019#include <linux/mod_devicetable.h>
Laura Abbottf8c03b92012-02-16 14:57:58 -080020#include <linux/platform_device.h>
Laura Abbottad340ff2012-01-04 14:23:48 -080021#include <linux/sched.h>
22#include <linux/slab.h>
23#include <linux/string.h>
Jeff Ohlstein4e249082012-03-21 15:12:20 -070024#include <linux/atomic.h>
Laura Abbottf7e44042012-06-22 12:50:32 -070025#include <linux/of.h>
Laura Abbottad340ff2012-01-04 14:23:48 -080026#include <asm/io.h>
27#include <asm-generic/sizes.h>
28#include <mach/memory.h>
29#include <mach/msm_rtb.h>
30#include <mach/system.h>
31
32#define SENTINEL_BYTE_1 0xFF
33#define SENTINEL_BYTE_2 0xAA
34#define SENTINEL_BYTE_3 0xFF
35
Laura Abbottf7e44042012-06-22 12:50:32 -070036#define RTB_COMPAT_STR "qcom,msm-rtb"
37
Laura Abbottad340ff2012-01-04 14:23:48 -080038/* Write
39 * 1) 3 bytes sentinel
40 * 2) 1 bytes of log type
41 * 3) 4 bytes of where the caller came from
42 * 4) 4 bytes index
43 * 4) 4 bytes extra data from the caller
44 *
45 * Total = 16 bytes.
46 */
47struct msm_rtb_layout {
48 unsigned char sentinel[3];
49 unsigned char log_type;
50 void *caller;
51 unsigned long idx;
52 void *data;
53} __attribute__ ((__packed__));
54
55
56struct msm_rtb_state {
57 struct msm_rtb_layout *rtb;
Laura Abbott8d087162013-03-12 16:43:39 -070058 phys_addr_t phys;
Laura Abbottad340ff2012-01-04 14:23:48 -080059 int nentries;
60 int size;
61 int enabled;
Laura Abbottaaa34a12012-02-14 15:48:47 -080062 int initialized;
Laura Abbottad340ff2012-01-04 14:23:48 -080063 uint32_t filter;
64 int step_size;
65};
66
67#if defined(CONFIG_MSM_RTB_SEPARATE_CPUS)
68DEFINE_PER_CPU(atomic_t, msm_rtb_idx_cpu);
69#else
70static atomic_t msm_rtb_idx;
71#endif
72
73struct msm_rtb_state msm_rtb = {
Laura Abbottaa9b60b2012-01-23 13:06:20 -080074 .filter = 1 << LOGK_LOGBUF,
Laura Abbottaaa34a12012-02-14 15:48:47 -080075 .enabled = 1,
Laura Abbottad340ff2012-01-04 14:23:48 -080076};
77
78module_param_named(filter, msm_rtb.filter, uint, 0644);
79module_param_named(enable, msm_rtb.enabled, int, 0644);
80
Laura Abbotta29d7312012-04-09 13:10:01 -070081static int msm_rtb_panic_notifier(struct notifier_block *this,
82 unsigned long event, void *ptr)
83{
84 msm_rtb.enabled = 0;
85 return NOTIFY_DONE;
86}
87
88static struct notifier_block msm_rtb_panic_blk = {
89 .notifier_call = msm_rtb_panic_notifier,
90};
91
Sarang Joshi31a77242013-08-28 12:57:00 -070092int notrace msm_rtb_event_should_log(enum logk_event_type log_type)
Laura Abbottad340ff2012-01-04 14:23:48 -080093{
Laura Abbottaaa34a12012-02-14 15:48:47 -080094 return msm_rtb.initialized && msm_rtb.enabled &&
Jeff Ohlstein4e249082012-03-21 15:12:20 -070095 ((1 << (log_type & ~LOGTYPE_NOPC)) & msm_rtb.filter);
Laura Abbottad340ff2012-01-04 14:23:48 -080096}
97EXPORT_SYMBOL(msm_rtb_event_should_log);
98
99static void msm_rtb_emit_sentinel(struct msm_rtb_layout *start)
100{
101 start->sentinel[0] = SENTINEL_BYTE_1;
102 start->sentinel[1] = SENTINEL_BYTE_2;
103 start->sentinel[2] = SENTINEL_BYTE_3;
104}
105
106static void msm_rtb_write_type(enum logk_event_type log_type,
107 struct msm_rtb_layout *start)
108{
109 start->log_type = (char)log_type;
110}
111
112static void msm_rtb_write_caller(void *caller, struct msm_rtb_layout *start)
113{
114 start->caller = caller;
115}
116
117static void msm_rtb_write_idx(unsigned long idx,
118 struct msm_rtb_layout *start)
119{
120 start->idx = idx;
121}
122
123static void msm_rtb_write_data(void *data, struct msm_rtb_layout *start)
124{
125 start->data = data;
126}
127
Jeff Ohlstein4e249082012-03-21 15:12:20 -0700128static void uncached_logk_pc_idx(enum logk_event_type log_type, void *caller,
129 void *data, int idx)
130{
131 struct msm_rtb_layout *start;
132
133 start = &msm_rtb.rtb[idx & (msm_rtb.nentries - 1)];
134
135 msm_rtb_emit_sentinel(start);
136 msm_rtb_write_type(log_type, start);
137 msm_rtb_write_caller(caller, start);
138 msm_rtb_write_idx(idx, start);
139 msm_rtb_write_data(data, start);
140 mb();
141
142 return;
143}
144
145static void uncached_logk_timestamp(int idx)
146{
147 unsigned long long timestamp;
148 void *timestamp_upper, *timestamp_lower;
149 timestamp = sched_clock();
150 timestamp_lower = (void *)lower_32_bits(timestamp);
151 timestamp_upper = (void *)upper_32_bits(timestamp);
152
153 uncached_logk_pc_idx(LOGK_TIMESTAMP|LOGTYPE_NOPC, timestamp_lower,
154 timestamp_upper, idx);
155}
156
Laura Abbottad340ff2012-01-04 14:23:48 -0800157#if defined(CONFIG_MSM_RTB_SEPARATE_CPUS)
158static int msm_rtb_get_idx(void)
159{
Jeff Ohlstein4e249082012-03-21 15:12:20 -0700160 int cpu, i, offset;
Laura Abbottad340ff2012-01-04 14:23:48 -0800161 atomic_t *index;
162
163 /*
164 * ideally we would use get_cpu but this is a close enough
165 * approximation for our purposes.
166 */
167 cpu = raw_smp_processor_id();
168
169 index = &per_cpu(msm_rtb_idx_cpu, cpu);
170
171 i = atomic_add_return(msm_rtb.step_size, index);
172 i -= msm_rtb.step_size;
173
Jeff Ohlstein4e249082012-03-21 15:12:20 -0700174 /* Check if index has wrapped around */
175 offset = (i & (msm_rtb.nentries - 1)) -
176 ((i - msm_rtb.step_size) & (msm_rtb.nentries - 1));
177 if (offset < 0) {
178 uncached_logk_timestamp(i);
179 i = atomic_add_return(msm_rtb.step_size, index);
180 i -= msm_rtb.step_size;
181 }
182
Laura Abbottad340ff2012-01-04 14:23:48 -0800183 return i;
184}
185#else
186static int msm_rtb_get_idx(void)
187{
Jeff Ohlstein4e249082012-03-21 15:12:20 -0700188 int i, offset;
Laura Abbottad340ff2012-01-04 14:23:48 -0800189
190 i = atomic_inc_return(&msm_rtb_idx);
191 i--;
192
Jeff Ohlstein4e249082012-03-21 15:12:20 -0700193 /* Check if index has wrapped around */
194 offset = (i & (msm_rtb.nentries - 1)) -
195 ((i - 1) & (msm_rtb.nentries - 1));
196 if (offset < 0) {
197 uncached_logk_timestamp(i);
198 i = atomic_inc_return(&msm_rtb_idx);
199 i--;
200 }
201
Laura Abbottad340ff2012-01-04 14:23:48 -0800202 return i;
203}
204#endif
205
Sarang Joshi31a77242013-08-28 12:57:00 -0700206int notrace uncached_logk_pc(enum logk_event_type log_type, void *caller,
Laura Abbottad340ff2012-01-04 14:23:48 -0800207 void *data)
208{
209 int i;
Laura Abbottad340ff2012-01-04 14:23:48 -0800210
211 if (!msm_rtb_event_should_log(log_type))
212 return 0;
213
214 i = msm_rtb_get_idx();
215
Jeff Ohlstein4e249082012-03-21 15:12:20 -0700216 uncached_logk_pc_idx(log_type, caller, data, i);
Laura Abbottad340ff2012-01-04 14:23:48 -0800217
218 return 1;
219}
220EXPORT_SYMBOL(uncached_logk_pc);
221
Sarang Joshi31a77242013-08-28 12:57:00 -0700222noinline int notrace uncached_logk(enum logk_event_type log_type, void *data)
Laura Abbottad340ff2012-01-04 14:23:48 -0800223{
224 return uncached_logk_pc(log_type, __builtin_return_address(0), data);
225}
226EXPORT_SYMBOL(uncached_logk);
227
Laura Abbottf8c03b92012-02-16 14:57:58 -0800228int msm_rtb_probe(struct platform_device *pdev)
Laura Abbottad340ff2012-01-04 14:23:48 -0800229{
Laura Abbottf8c03b92012-02-16 14:57:58 -0800230 struct msm_rtb_platform_data *d = pdev->dev.platform_data;
Laura Abbottad340ff2012-01-04 14:23:48 -0800231#if defined(CONFIG_MSM_RTB_SEPARATE_CPUS)
232 unsigned int cpu;
233#endif
Laura Abbottf7e44042012-06-22 12:50:32 -0700234 int ret;
Laura Abbottad340ff2012-01-04 14:23:48 -0800235
Laura Abbottf7e44042012-06-22 12:50:32 -0700236 if (!pdev->dev.of_node) {
237 msm_rtb.size = d->size;
238 } else {
239 int size;
240
241 ret = of_property_read_u32((&pdev->dev)->of_node,
242 "qcom,memory-reservation-size",
243 &size);
244
245 if (ret < 0)
246 return ret;
247
248 msm_rtb.size = size;
249 }
Laura Abbottf8c03b92012-02-16 14:57:58 -0800250
Laura Abbottad340ff2012-01-04 14:23:48 -0800251 if (msm_rtb.size <= 0 || msm_rtb.size > SZ_1M)
252 return -EINVAL;
253
254 /*
255 * The ioremap call is made separately to store the physical
256 * address of the buffer. This is necessary for cases where
257 * the only way to access the buffer is a physical address.
258 */
259 msm_rtb.phys = allocate_contiguous_ebi_nomap(msm_rtb.size, SZ_4K);
260
261 if (!msm_rtb.phys)
262 return -ENOMEM;
263
264 msm_rtb.rtb = ioremap(msm_rtb.phys, msm_rtb.size);
265
266 if (!msm_rtb.rtb) {
267 free_contiguous_memory_by_paddr(msm_rtb.phys);
268 return -ENOMEM;
269 }
270
271 msm_rtb.nentries = msm_rtb.size / sizeof(struct msm_rtb_layout);
272
273 /* Round this down to a power of 2 */
274 msm_rtb.nentries = __rounddown_pow_of_two(msm_rtb.nentries);
275
276 memset(msm_rtb.rtb, 0, msm_rtb.size);
277
278
279#if defined(CONFIG_MSM_RTB_SEPARATE_CPUS)
280 for_each_possible_cpu(cpu) {
281 atomic_t *a = &per_cpu(msm_rtb_idx_cpu, cpu);
282 atomic_set(a, cpu);
283 }
284 msm_rtb.step_size = num_possible_cpus();
285#else
286 atomic_set(&msm_rtb_idx, 0);
287 msm_rtb.step_size = 1;
288#endif
289
Laura Abbotta29d7312012-04-09 13:10:01 -0700290 atomic_notifier_chain_register(&panic_notifier_list,
291 &msm_rtb_panic_blk);
Laura Abbottaaa34a12012-02-14 15:48:47 -0800292 msm_rtb.initialized = 1;
Laura Abbottad340ff2012-01-04 14:23:48 -0800293 return 0;
294}
Laura Abbottf8c03b92012-02-16 14:57:58 -0800295
Laura Abbottf7e44042012-06-22 12:50:32 -0700296static struct of_device_id msm_match_table[] = {
297 {.compatible = RTB_COMPAT_STR},
298 {},
299};
300EXPORT_COMPAT(RTB_COMPAT_STR);
301
Laura Abbottf8c03b92012-02-16 14:57:58 -0800302static struct platform_driver msm_rtb_driver = {
303 .driver = {
304 .name = "msm_rtb",
Laura Abbottf7e44042012-06-22 12:50:32 -0700305 .owner = THIS_MODULE,
306 .of_match_table = msm_match_table
Laura Abbottf8c03b92012-02-16 14:57:58 -0800307 },
308};
309
310static int __init msm_rtb_init(void)
311{
312 return platform_driver_probe(&msm_rtb_driver, msm_rtb_probe);
313}
314
315static void __exit msm_rtb_exit(void)
316{
317 platform_driver_unregister(&msm_rtb_driver);
318}
Laura Abbottad340ff2012-01-04 14:23:48 -0800319module_init(msm_rtb_init)
Laura Abbottf8c03b92012-02-16 14:57:58 -0800320module_exit(msm_rtb_exit)