blob: 9dbf9c1ebed02c550dbb41f83a3839c9a472548f [file] [log] [blame]
Laura Abbottad340ff2012-01-04 14:23:48 -08001/*
2 * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
3 *
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 Abbottf8c03b92012-02-16 14:57:58 -080019#include <linux/platform_device.h>
Laura Abbottad340ff2012-01-04 14:23:48 -080020#include <linux/sched.h>
21#include <linux/slab.h>
22#include <linux/string.h>
Jeff Ohlstein4e249082012-03-21 15:12:20 -070023#include <linux/atomic.h>
Laura Abbottad340ff2012-01-04 14:23:48 -080024#include <asm/io.h>
25#include <asm-generic/sizes.h>
26#include <mach/memory.h>
27#include <mach/msm_rtb.h>
28#include <mach/system.h>
29
30#define SENTINEL_BYTE_1 0xFF
31#define SENTINEL_BYTE_2 0xAA
32#define SENTINEL_BYTE_3 0xFF
33
34/* Write
35 * 1) 3 bytes sentinel
36 * 2) 1 bytes of log type
37 * 3) 4 bytes of where the caller came from
38 * 4) 4 bytes index
39 * 4) 4 bytes extra data from the caller
40 *
41 * Total = 16 bytes.
42 */
43struct msm_rtb_layout {
44 unsigned char sentinel[3];
45 unsigned char log_type;
46 void *caller;
47 unsigned long idx;
48 void *data;
49} __attribute__ ((__packed__));
50
51
52struct msm_rtb_state {
53 struct msm_rtb_layout *rtb;
54 unsigned long phys;
55 int nentries;
56 int size;
57 int enabled;
Laura Abbottaaa34a12012-02-14 15:48:47 -080058 int initialized;
Laura Abbottad340ff2012-01-04 14:23:48 -080059 uint32_t filter;
60 int step_size;
61};
62
63#if defined(CONFIG_MSM_RTB_SEPARATE_CPUS)
64DEFINE_PER_CPU(atomic_t, msm_rtb_idx_cpu);
65#else
66static atomic_t msm_rtb_idx;
67#endif
68
69struct msm_rtb_state msm_rtb = {
Laura Abbottaa9b60b2012-01-23 13:06:20 -080070 .filter = 1 << LOGK_LOGBUF,
Laura Abbottaaa34a12012-02-14 15:48:47 -080071 .enabled = 1,
Laura Abbottad340ff2012-01-04 14:23:48 -080072};
73
74module_param_named(filter, msm_rtb.filter, uint, 0644);
75module_param_named(enable, msm_rtb.enabled, int, 0644);
76
Laura Abbotta29d7312012-04-09 13:10:01 -070077static int msm_rtb_panic_notifier(struct notifier_block *this,
78 unsigned long event, void *ptr)
79{
80 msm_rtb.enabled = 0;
81 return NOTIFY_DONE;
82}
83
84static struct notifier_block msm_rtb_panic_blk = {
85 .notifier_call = msm_rtb_panic_notifier,
86};
87
Laura Abbottad340ff2012-01-04 14:23:48 -080088int msm_rtb_event_should_log(enum logk_event_type log_type)
89{
Laura Abbottaaa34a12012-02-14 15:48:47 -080090 return msm_rtb.initialized && msm_rtb.enabled &&
Jeff Ohlstein4e249082012-03-21 15:12:20 -070091 ((1 << (log_type & ~LOGTYPE_NOPC)) & msm_rtb.filter);
Laura Abbottad340ff2012-01-04 14:23:48 -080092}
93EXPORT_SYMBOL(msm_rtb_event_should_log);
94
95static void msm_rtb_emit_sentinel(struct msm_rtb_layout *start)
96{
97 start->sentinel[0] = SENTINEL_BYTE_1;
98 start->sentinel[1] = SENTINEL_BYTE_2;
99 start->sentinel[2] = SENTINEL_BYTE_3;
100}
101
102static void msm_rtb_write_type(enum logk_event_type log_type,
103 struct msm_rtb_layout *start)
104{
105 start->log_type = (char)log_type;
106}
107
108static void msm_rtb_write_caller(void *caller, struct msm_rtb_layout *start)
109{
110 start->caller = caller;
111}
112
113static void msm_rtb_write_idx(unsigned long idx,
114 struct msm_rtb_layout *start)
115{
116 start->idx = idx;
117}
118
119static void msm_rtb_write_data(void *data, struct msm_rtb_layout *start)
120{
121 start->data = data;
122}
123
Jeff Ohlstein4e249082012-03-21 15:12:20 -0700124static void uncached_logk_pc_idx(enum logk_event_type log_type, void *caller,
125 void *data, int idx)
126{
127 struct msm_rtb_layout *start;
128
129 start = &msm_rtb.rtb[idx & (msm_rtb.nentries - 1)];
130
131 msm_rtb_emit_sentinel(start);
132 msm_rtb_write_type(log_type, start);
133 msm_rtb_write_caller(caller, start);
134 msm_rtb_write_idx(idx, start);
135 msm_rtb_write_data(data, start);
136 mb();
137
138 return;
139}
140
141static void uncached_logk_timestamp(int idx)
142{
143 unsigned long long timestamp;
144 void *timestamp_upper, *timestamp_lower;
145 timestamp = sched_clock();
146 timestamp_lower = (void *)lower_32_bits(timestamp);
147 timestamp_upper = (void *)upper_32_bits(timestamp);
148
149 uncached_logk_pc_idx(LOGK_TIMESTAMP|LOGTYPE_NOPC, timestamp_lower,
150 timestamp_upper, idx);
151}
152
Laura Abbottad340ff2012-01-04 14:23:48 -0800153#if defined(CONFIG_MSM_RTB_SEPARATE_CPUS)
154static int msm_rtb_get_idx(void)
155{
Jeff Ohlstein4e249082012-03-21 15:12:20 -0700156 int cpu, i, offset;
Laura Abbottad340ff2012-01-04 14:23:48 -0800157 atomic_t *index;
158
159 /*
160 * ideally we would use get_cpu but this is a close enough
161 * approximation for our purposes.
162 */
163 cpu = raw_smp_processor_id();
164
165 index = &per_cpu(msm_rtb_idx_cpu, cpu);
166
167 i = atomic_add_return(msm_rtb.step_size, index);
168 i -= msm_rtb.step_size;
169
Jeff Ohlstein4e249082012-03-21 15:12:20 -0700170 /* Check if index has wrapped around */
171 offset = (i & (msm_rtb.nentries - 1)) -
172 ((i - msm_rtb.step_size) & (msm_rtb.nentries - 1));
173 if (offset < 0) {
174 uncached_logk_timestamp(i);
175 i = atomic_add_return(msm_rtb.step_size, index);
176 i -= msm_rtb.step_size;
177 }
178
Laura Abbottad340ff2012-01-04 14:23:48 -0800179 return i;
180}
181#else
182static int msm_rtb_get_idx(void)
183{
Jeff Ohlstein4e249082012-03-21 15:12:20 -0700184 int i, offset;
Laura Abbottad340ff2012-01-04 14:23:48 -0800185
186 i = atomic_inc_return(&msm_rtb_idx);
187 i--;
188
Jeff Ohlstein4e249082012-03-21 15:12:20 -0700189 /* Check if index has wrapped around */
190 offset = (i & (msm_rtb.nentries - 1)) -
191 ((i - 1) & (msm_rtb.nentries - 1));
192 if (offset < 0) {
193 uncached_logk_timestamp(i);
194 i = atomic_inc_return(&msm_rtb_idx);
195 i--;
196 }
197
Laura Abbottad340ff2012-01-04 14:23:48 -0800198 return i;
199}
200#endif
201
202int uncached_logk_pc(enum logk_event_type log_type, void *caller,
203 void *data)
204{
205 int i;
Laura Abbottad340ff2012-01-04 14:23:48 -0800206
207 if (!msm_rtb_event_should_log(log_type))
208 return 0;
209
210 i = msm_rtb_get_idx();
211
Jeff Ohlstein4e249082012-03-21 15:12:20 -0700212 uncached_logk_pc_idx(log_type, caller, data, i);
Laura Abbottad340ff2012-01-04 14:23:48 -0800213
214 return 1;
215}
216EXPORT_SYMBOL(uncached_logk_pc);
217
218noinline int uncached_logk(enum logk_event_type log_type, void *data)
219{
220 return uncached_logk_pc(log_type, __builtin_return_address(0), data);
221}
222EXPORT_SYMBOL(uncached_logk);
223
Laura Abbottf8c03b92012-02-16 14:57:58 -0800224int msm_rtb_probe(struct platform_device *pdev)
Laura Abbottad340ff2012-01-04 14:23:48 -0800225{
Laura Abbottf8c03b92012-02-16 14:57:58 -0800226 struct msm_rtb_platform_data *d = pdev->dev.platform_data;
Laura Abbottad340ff2012-01-04 14:23:48 -0800227#if defined(CONFIG_MSM_RTB_SEPARATE_CPUS)
228 unsigned int cpu;
229#endif
230
Laura Abbottf8c03b92012-02-16 14:57:58 -0800231 msm_rtb.size = d->size;
232
Laura Abbottad340ff2012-01-04 14:23:48 -0800233 if (msm_rtb.size <= 0 || msm_rtb.size > SZ_1M)
234 return -EINVAL;
235
236 /*
237 * The ioremap call is made separately to store the physical
238 * address of the buffer. This is necessary for cases where
239 * the only way to access the buffer is a physical address.
240 */
241 msm_rtb.phys = allocate_contiguous_ebi_nomap(msm_rtb.size, SZ_4K);
242
243 if (!msm_rtb.phys)
244 return -ENOMEM;
245
246 msm_rtb.rtb = ioremap(msm_rtb.phys, msm_rtb.size);
247
248 if (!msm_rtb.rtb) {
249 free_contiguous_memory_by_paddr(msm_rtb.phys);
250 return -ENOMEM;
251 }
252
253 msm_rtb.nentries = msm_rtb.size / sizeof(struct msm_rtb_layout);
254
255 /* Round this down to a power of 2 */
256 msm_rtb.nentries = __rounddown_pow_of_two(msm_rtb.nentries);
257
258 memset(msm_rtb.rtb, 0, msm_rtb.size);
259
260
261#if defined(CONFIG_MSM_RTB_SEPARATE_CPUS)
262 for_each_possible_cpu(cpu) {
263 atomic_t *a = &per_cpu(msm_rtb_idx_cpu, cpu);
264 atomic_set(a, cpu);
265 }
266 msm_rtb.step_size = num_possible_cpus();
267#else
268 atomic_set(&msm_rtb_idx, 0);
269 msm_rtb.step_size = 1;
270#endif
271
Laura Abbotta29d7312012-04-09 13:10:01 -0700272 atomic_notifier_chain_register(&panic_notifier_list,
273 &msm_rtb_panic_blk);
Laura Abbottaaa34a12012-02-14 15:48:47 -0800274 msm_rtb.initialized = 1;
Laura Abbottad340ff2012-01-04 14:23:48 -0800275 return 0;
276}
Laura Abbottf8c03b92012-02-16 14:57:58 -0800277
278static struct platform_driver msm_rtb_driver = {
279 .driver = {
280 .name = "msm_rtb",
281 .owner = THIS_MODULE
282 },
283};
284
285static int __init msm_rtb_init(void)
286{
287 return platform_driver_probe(&msm_rtb_driver, msm_rtb_probe);
288}
289
290static void __exit msm_rtb_exit(void)
291{
292 platform_driver_unregister(&msm_rtb_driver);
293}
Laura Abbottad340ff2012-01-04 14:23:48 -0800294module_init(msm_rtb_init)
Laura Abbottf8c03b92012-02-16 14:57:58 -0800295module_exit(msm_rtb_exit)