blob: 403c13d1a1fb6c5b264fd177de3b049356c61183 [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
77int msm_rtb_event_should_log(enum logk_event_type log_type)
78{
Laura Abbottaaa34a12012-02-14 15:48:47 -080079 return msm_rtb.initialized && msm_rtb.enabled &&
Jeff Ohlstein4e249082012-03-21 15:12:20 -070080 ((1 << (log_type & ~LOGTYPE_NOPC)) & msm_rtb.filter);
Laura Abbottad340ff2012-01-04 14:23:48 -080081}
82EXPORT_SYMBOL(msm_rtb_event_should_log);
83
84static void msm_rtb_emit_sentinel(struct msm_rtb_layout *start)
85{
86 start->sentinel[0] = SENTINEL_BYTE_1;
87 start->sentinel[1] = SENTINEL_BYTE_2;
88 start->sentinel[2] = SENTINEL_BYTE_3;
89}
90
91static void msm_rtb_write_type(enum logk_event_type log_type,
92 struct msm_rtb_layout *start)
93{
94 start->log_type = (char)log_type;
95}
96
97static void msm_rtb_write_caller(void *caller, struct msm_rtb_layout *start)
98{
99 start->caller = caller;
100}
101
102static void msm_rtb_write_idx(unsigned long idx,
103 struct msm_rtb_layout *start)
104{
105 start->idx = idx;
106}
107
108static void msm_rtb_write_data(void *data, struct msm_rtb_layout *start)
109{
110 start->data = data;
111}
112
Jeff Ohlstein4e249082012-03-21 15:12:20 -0700113static void uncached_logk_pc_idx(enum logk_event_type log_type, void *caller,
114 void *data, int idx)
115{
116 struct msm_rtb_layout *start;
117
118 start = &msm_rtb.rtb[idx & (msm_rtb.nentries - 1)];
119
120 msm_rtb_emit_sentinel(start);
121 msm_rtb_write_type(log_type, start);
122 msm_rtb_write_caller(caller, start);
123 msm_rtb_write_idx(idx, start);
124 msm_rtb_write_data(data, start);
125 mb();
126
127 return;
128}
129
130static void uncached_logk_timestamp(int idx)
131{
132 unsigned long long timestamp;
133 void *timestamp_upper, *timestamp_lower;
134 timestamp = sched_clock();
135 timestamp_lower = (void *)lower_32_bits(timestamp);
136 timestamp_upper = (void *)upper_32_bits(timestamp);
137
138 uncached_logk_pc_idx(LOGK_TIMESTAMP|LOGTYPE_NOPC, timestamp_lower,
139 timestamp_upper, idx);
140}
141
Laura Abbottad340ff2012-01-04 14:23:48 -0800142#if defined(CONFIG_MSM_RTB_SEPARATE_CPUS)
143static int msm_rtb_get_idx(void)
144{
Jeff Ohlstein4e249082012-03-21 15:12:20 -0700145 int cpu, i, offset;
Laura Abbottad340ff2012-01-04 14:23:48 -0800146 atomic_t *index;
147
148 /*
149 * ideally we would use get_cpu but this is a close enough
150 * approximation for our purposes.
151 */
152 cpu = raw_smp_processor_id();
153
154 index = &per_cpu(msm_rtb_idx_cpu, cpu);
155
156 i = atomic_add_return(msm_rtb.step_size, index);
157 i -= msm_rtb.step_size;
158
Jeff Ohlstein4e249082012-03-21 15:12:20 -0700159 /* Check if index has wrapped around */
160 offset = (i & (msm_rtb.nentries - 1)) -
161 ((i - msm_rtb.step_size) & (msm_rtb.nentries - 1));
162 if (offset < 0) {
163 uncached_logk_timestamp(i);
164 i = atomic_add_return(msm_rtb.step_size, index);
165 i -= msm_rtb.step_size;
166 }
167
Laura Abbottad340ff2012-01-04 14:23:48 -0800168 return i;
169}
170#else
171static int msm_rtb_get_idx(void)
172{
Jeff Ohlstein4e249082012-03-21 15:12:20 -0700173 int i, offset;
Laura Abbottad340ff2012-01-04 14:23:48 -0800174
175 i = atomic_inc_return(&msm_rtb_idx);
176 i--;
177
Jeff Ohlstein4e249082012-03-21 15:12:20 -0700178 /* Check if index has wrapped around */
179 offset = (i & (msm_rtb.nentries - 1)) -
180 ((i - 1) & (msm_rtb.nentries - 1));
181 if (offset < 0) {
182 uncached_logk_timestamp(i);
183 i = atomic_inc_return(&msm_rtb_idx);
184 i--;
185 }
186
Laura Abbottad340ff2012-01-04 14:23:48 -0800187 return i;
188}
189#endif
190
191int uncached_logk_pc(enum logk_event_type log_type, void *caller,
192 void *data)
193{
194 int i;
Laura Abbottad340ff2012-01-04 14:23:48 -0800195
196 if (!msm_rtb_event_should_log(log_type))
197 return 0;
198
199 i = msm_rtb_get_idx();
200
Jeff Ohlstein4e249082012-03-21 15:12:20 -0700201 uncached_logk_pc_idx(log_type, caller, data, i);
Laura Abbottad340ff2012-01-04 14:23:48 -0800202
203 return 1;
204}
205EXPORT_SYMBOL(uncached_logk_pc);
206
207noinline int uncached_logk(enum logk_event_type log_type, void *data)
208{
209 return uncached_logk_pc(log_type, __builtin_return_address(0), data);
210}
211EXPORT_SYMBOL(uncached_logk);
212
Laura Abbottf8c03b92012-02-16 14:57:58 -0800213int msm_rtb_probe(struct platform_device *pdev)
Laura Abbottad340ff2012-01-04 14:23:48 -0800214{
Laura Abbottf8c03b92012-02-16 14:57:58 -0800215 struct msm_rtb_platform_data *d = pdev->dev.platform_data;
Laura Abbottad340ff2012-01-04 14:23:48 -0800216#if defined(CONFIG_MSM_RTB_SEPARATE_CPUS)
217 unsigned int cpu;
218#endif
219
Laura Abbottf8c03b92012-02-16 14:57:58 -0800220 msm_rtb.size = d->size;
221
Laura Abbottad340ff2012-01-04 14:23:48 -0800222 if (msm_rtb.size <= 0 || msm_rtb.size > SZ_1M)
223 return -EINVAL;
224
225 /*
226 * The ioremap call is made separately to store the physical
227 * address of the buffer. This is necessary for cases where
228 * the only way to access the buffer is a physical address.
229 */
230 msm_rtb.phys = allocate_contiguous_ebi_nomap(msm_rtb.size, SZ_4K);
231
232 if (!msm_rtb.phys)
233 return -ENOMEM;
234
235 msm_rtb.rtb = ioremap(msm_rtb.phys, msm_rtb.size);
236
237 if (!msm_rtb.rtb) {
238 free_contiguous_memory_by_paddr(msm_rtb.phys);
239 return -ENOMEM;
240 }
241
242 msm_rtb.nentries = msm_rtb.size / sizeof(struct msm_rtb_layout);
243
244 /* Round this down to a power of 2 */
245 msm_rtb.nentries = __rounddown_pow_of_two(msm_rtb.nentries);
246
247 memset(msm_rtb.rtb, 0, msm_rtb.size);
248
249
250#if defined(CONFIG_MSM_RTB_SEPARATE_CPUS)
251 for_each_possible_cpu(cpu) {
252 atomic_t *a = &per_cpu(msm_rtb_idx_cpu, cpu);
253 atomic_set(a, cpu);
254 }
255 msm_rtb.step_size = num_possible_cpus();
256#else
257 atomic_set(&msm_rtb_idx, 0);
258 msm_rtb.step_size = 1;
259#endif
260
261
Laura Abbottaaa34a12012-02-14 15:48:47 -0800262 msm_rtb.initialized = 1;
Laura Abbottad340ff2012-01-04 14:23:48 -0800263 return 0;
264}
Laura Abbottf8c03b92012-02-16 14:57:58 -0800265
266static struct platform_driver msm_rtb_driver = {
267 .driver = {
268 .name = "msm_rtb",
269 .owner = THIS_MODULE
270 },
271};
272
273static int __init msm_rtb_init(void)
274{
275 return platform_driver_probe(&msm_rtb_driver, msm_rtb_probe);
276}
277
278static void __exit msm_rtb_exit(void)
279{
280 platform_driver_unregister(&msm_rtb_driver);
281}
Laura Abbottad340ff2012-01-04 14:23:48 -0800282module_init(msm_rtb_init)
Laura Abbottf8c03b92012-02-16 14:57:58 -0800283module_exit(msm_rtb_exit)