blob: 8f4b6f8d52294e1a39f77deede6137fee557601e [file] [log] [blame]
Pratik Patel5ecf6a12012-04-25 18:34:59 -07001/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/kernel.h>
14#include <linux/module.h>
15#include <linux/init.h>
16#include <linux/types.h>
17#include <linux/device.h>
18#include <linux/platform_device.h>
19#include <linux/io.h>
20#include <linux/err.h>
21#include <linux/fs.h>
22#include <linux/miscdevice.h>
23#include <linux/uaccess.h>
24#include <linux/slab.h>
Pratik Patelf17b1472012-05-25 22:23:52 -070025#include <linux/clk.h>
Pratik Patel43e47bd2012-05-19 18:10:55 -070026#include <linux/cs.h>
Pratik Patel05490932012-05-20 18:20:19 -070027#include <linux/cs-stm.h>
Pratik Patel5ecf6a12012-04-25 18:34:59 -070028#include <asm/unaligned.h>
Pratik Patel5ecf6a12012-04-25 18:34:59 -070029
Pratik Patel33454d22012-05-18 10:48:17 -070030#include "cs-priv.h"
Pratik Patel5ecf6a12012-04-25 18:34:59 -070031
32#define stm_writel(stm, val, off) \
33 __raw_writel((val), stm.base + off)
34#define stm_readl(stm, val, off) \
35 __raw_readl(stm.base + off)
36
37#define NR_STM_CHANNEL (32)
38#define BYTES_PER_CHANNEL (256)
39
40enum {
41 STM_PKT_TYPE_DATA = 0x98,
42 STM_PKT_TYPE_FLAG = 0xE8,
43 STM_PKT_TYPE_TRIG = 0xF8,
44};
45
46enum {
47 STM_OPTION_MARKED = 0x10,
48};
49
50#define STM_TRACE_BUF_SIZE (1024)
51
52#define OST_START_TOKEN (0x30)
53#define OST_VERSION (0x1)
54
55#define stm_channel_addr(ch) \
56 (stm.chs.base + (ch * BYTES_PER_CHANNEL))
57#define stm_channel_off(type, opts) (type & ~opts)
58
59#define STM_LOCK() \
60do { \
61 mb(); \
62 stm_writel(stm, 0x0, CS_LAR); \
63} while (0)
64#define STM_UNLOCK() \
65do { \
66 stm_writel(stm, CS_UNLOCK_MAGIC, CS_LAR); \
67 mb(); \
68} while (0)
69
70#define STMSPER (0xE00)
71#define STMSPTER (0xE20)
72#define STMTCSR (0xE80)
73#define STMSYNCR (0xE90)
74
75#ifdef CONFIG_MSM_QDSS_STM_DEFAULT_ENABLE
76static int stm_boot_enable = 1;
77#else
78static int stm_boot_enable;
79#endif
80
81module_param_named(
82 stm_boot_enable, stm_boot_enable, int, S_IRUGO
83);
84
85static int stm_boot_nr_channel;
86
87module_param_named(
88 stm_boot_nr_channel, stm_boot_nr_channel, int, S_IRUGO
89);
90
91struct channel_space {
92 void __iomem *base;
93 unsigned long *bitmap;
94};
95
96struct stm_ctx {
97 void __iomem *base;
98 bool enabled;
99 struct qdss_source *src;
100 struct device *dev;
101 struct kobject *kobj;
Pratik Patelf17b1472012-05-25 22:23:52 -0700102 struct clk *clk;
Pratik Patel5ecf6a12012-04-25 18:34:59 -0700103 uint32_t entity;
104 struct channel_space chs;
105};
106
107static struct stm_ctx stm = {
108 .entity = OST_ENTITY_ALL,
109};
110
111
112static void __stm_enable(void)
113{
114 STM_UNLOCK();
115
116 stm_writel(stm, 0x80, STMSYNCR);
117 stm_writel(stm, 0xFFFFFFFF, STMSPTER);
118 stm_writel(stm, 0xFFFFFFFF, STMSPER);
119 stm_writel(stm, 0x30003, STMTCSR);
120
121 STM_LOCK();
122}
123
124static int stm_enable(void)
125{
126 int ret;
127
128 if (stm.enabled) {
129 dev_err(stm.dev, "STM tracing already enabled\n");
130 ret = -EINVAL;
131 goto err;
132 }
133
Pratik Patelf17b1472012-05-25 22:23:52 -0700134 ret = clk_prepare_enable(stm.clk);
135 if (ret)
136 goto err_clk;
137
Pratik Patel5ecf6a12012-04-25 18:34:59 -0700138 ret = qdss_enable(stm.src);
139 if (ret)
Pratik Patelf17b1472012-05-25 22:23:52 -0700140 goto err_qdss;
Pratik Patel5ecf6a12012-04-25 18:34:59 -0700141
142 __stm_enable();
143
144 stm.enabled = true;
145
146 dev_info(stm.dev, "STM tracing enabled\n");
147 return 0;
148
Pratik Patelf17b1472012-05-25 22:23:52 -0700149err_qdss:
150 clk_disable_unprepare(stm.clk);
151err_clk:
Pratik Patel5ecf6a12012-04-25 18:34:59 -0700152err:
153 return ret;
154}
155
156static void __stm_disable(void)
157{
158 STM_UNLOCK();
159
160 stm_writel(stm, 0x30000, STMTCSR);
161 stm_writel(stm, 0x0, STMSPER);
162 stm_writel(stm, 0x0, STMSPTER);
163
164 STM_LOCK();
165}
166
167static int stm_disable(void)
168{
169 int ret;
170
171 if (!stm.enabled) {
172 dev_err(stm.dev, "STM tracing already disabled\n");
173 ret = -EINVAL;
174 goto err;
175 }
176
177 __stm_disable();
178
Pratik Patelf17b1472012-05-25 22:23:52 -0700179 stm.enabled = false;
180
Pratik Patel5ecf6a12012-04-25 18:34:59 -0700181 qdss_disable(stm.src);
182
Pratik Patelf17b1472012-05-25 22:23:52 -0700183 clk_disable_unprepare(stm.clk);
Pratik Patel5ecf6a12012-04-25 18:34:59 -0700184
185 dev_info(stm.dev, "STM tracing disabled\n");
186 return 0;
187
188err:
189 return ret;
190}
191
192static uint32_t stm_channel_alloc(uint32_t off)
193{
194 uint32_t ch;
195
196 do {
197 ch = find_next_zero_bit(stm.chs.bitmap, NR_STM_CHANNEL, off);
198 } while ((ch < NR_STM_CHANNEL) && test_and_set_bit(ch, stm.chs.bitmap));
199
200 return ch;
201}
202
203static void stm_channel_free(uint32_t ch)
204{
205 clear_bit(ch, stm.chs.bitmap);
206}
207
208static int stm_send(void *addr, const void *data, uint32_t size)
209{
210 uint64_t prepad = 0;
211 uint64_t postpad = 0;
212 char *pad;
213 uint8_t off, endoff;
214 uint32_t len = size;
215
216 /* only 64bit writes are supported, we rely on the compiler to
217 * generate STRD instruction for the casted 64bit assignments
218 */
219
220 off = (unsigned long)data & 0x7;
221
222 if (off) {
223 endoff = 8 - off;
224 pad = (char *)&prepad;
225 pad += off;
226
227 while (endoff && size) {
228 *pad++ = *(char *)data++;
229 endoff--;
230 size--;
231 }
232 *(volatile uint64_t __force *)addr = prepad;
233 }
234
235 /* now we are 64bit aligned */
236 while (size >= 8) {
237 *(volatile uint64_t __force *)addr = *(uint64_t *)data;
238 data += 8;
239 size -= 8;
240 }
241
242 if (size) {
243 pad = (char *)&postpad;
244
245 while (size) {
246 *pad++ = *(char *)data++;
247 size--;
248 }
249 *(volatile uint64_t __force *)addr = postpad;
250 }
251
252 return roundup(len + off, 8);
253}
254
255static int stm_trace_ost_header(unsigned long ch_addr, uint32_t options,
256 uint8_t entity_id, uint8_t proto_id,
257 const void *payload_data, uint32_t payload_size)
258{
259 void *addr;
260 uint8_t prepad_size;
261 uint64_t header;
262 char *hdr;
263
264 hdr = (char *)&header;
265
266 hdr[0] = OST_START_TOKEN;
267 hdr[1] = OST_VERSION;
268 hdr[2] = entity_id;
269 hdr[3] = proto_id;
270 prepad_size = (unsigned long)payload_data & 0x7;
271 *(uint32_t *)(hdr + 4) = (prepad_size << 24) | payload_size;
272
273 /* for 64bit writes, header is expected to be of the D32M, D32M */
274 options |= STM_OPTION_MARKED;
275 options &= ~STM_OPTION_TIMESTAMPED;
276 addr = (void *)(ch_addr | stm_channel_off(STM_PKT_TYPE_DATA, options));
277
278 return stm_send(addr, &header, sizeof(header));
279}
280
281static int stm_trace_data(unsigned long ch_addr, uint32_t options,
282 const void *data, uint32_t size)
283{
284 void *addr;
285
286 options &= ~STM_OPTION_TIMESTAMPED;
287 addr = (void *)(ch_addr | stm_channel_off(STM_PKT_TYPE_DATA, options));
288
289 return stm_send(addr, data, size);
290}
291
292static int stm_trace_ost_tail(unsigned long ch_addr, uint32_t options)
293{
294 void *addr;
295 uint64_t tail = 0x0;
296
297 addr = (void *)(ch_addr | stm_channel_off(STM_PKT_TYPE_FLAG, options));
298
299 return stm_send(addr, &tail, sizeof(tail));
300}
301
302static inline int __stm_trace(uint32_t options, uint8_t entity_id,
303 uint8_t proto_id, const void *data, uint32_t size)
304{
305 int len = 0;
306 uint32_t ch;
307 unsigned long ch_addr;
308
309 /* allocate channel and get the channel address */
310 ch = stm_channel_alloc(0);
311 ch_addr = (unsigned long)stm_channel_addr(ch);
312
313 /* send the ost header */
314 len += stm_trace_ost_header(ch_addr, options, entity_id, proto_id, data,
315 size);
316
317 /* send the payload data */
318 len += stm_trace_data(ch_addr, options, data, size);
319
320 /* send the ost tail */
321 len += stm_trace_ost_tail(ch_addr, options);
322
323 /* we are done, free the channel */
324 stm_channel_free(ch);
325
326 return len;
327}
328
329/**
330 * stm_trace - trace the binary or string data through STM
331 * @options: tracing options - guaranteed, timestamped, etc
332 * @entity_id: entity representing the trace data
333 * @proto_id: protocol id to distinguish between different binary formats
334 * @data: pointer to binary or string data buffer
335 * @size: size of data to send
336 *
337 * Packetizes the data as the payload to an OST packet and sends it over STM
338 *
339 * CONTEXT:
340 * Can be called from any context.
341 *
342 * RETURNS:
343 * number of bytes transfered over STM
344 */
345int stm_trace(uint32_t options, uint8_t entity_id, uint8_t proto_id,
346 const void *data, uint32_t size)
347{
348 /* we don't support sizes more than 24bits (0 to 23) */
349 if (!(stm.enabled && (stm.entity & entity_id) &&
350 (size < 0x1000000)))
351 return 0;
352
353 return __stm_trace(options, entity_id, proto_id, data, size);
354}
355EXPORT_SYMBOL(stm_trace);
356
357static ssize_t stm_write(struct file *file, const char __user *data,
358 size_t size, loff_t *ppos)
359{
360 char *buf;
361
362 if (!stm.enabled)
363 return -EINVAL;
364
365 if (!(stm.entity & OST_ENTITY_DEV_NODE))
366 return size;
367
368 if (size > STM_TRACE_BUF_SIZE)
369 size = STM_TRACE_BUF_SIZE;
370
371 buf = kmalloc(size, GFP_KERNEL);
372 if (!buf)
373 return -ENOMEM;
374
375 if (copy_from_user(buf, data, size)) {
376 kfree(buf);
377 dev_dbg(stm.dev, "%s: copy_from_user failed\n", __func__);
378 return -EFAULT;
379 }
380
381 __stm_trace(STM_OPTION_TIMESTAMPED, OST_ENTITY_DEV_NODE, 0, buf, size);
382
383 kfree(buf);
384
385 return size;
386}
387
388static const struct file_operations stm_fops = {
389 .owner = THIS_MODULE,
390 .write = stm_write,
391 .llseek = no_llseek,
392};
393
394static struct miscdevice stm_misc = {
395 .name = "msm_stm",
396 .minor = MISC_DYNAMIC_MINOR,
397 .fops = &stm_fops,
398};
399
Pratik Patela9c0e062012-05-28 13:45:35 -0700400static ssize_t stm_show_enabled(struct device *dev,
401 struct device_attribute *attr, char *buf)
402{
403 unsigned long val = stm.enabled;
404 return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
405}
Pratik Patel5ecf6a12012-04-25 18:34:59 -0700406
Pratik Patela9c0e062012-05-28 13:45:35 -0700407static ssize_t stm_store_enabled(struct device *dev,
408 struct device_attribute *attr,
409 const char *buf, size_t size)
Pratik Patel5ecf6a12012-04-25 18:34:59 -0700410{
411 int ret = 0;
412 unsigned long val;
413
414 if (sscanf(buf, "%lx", &val) != 1)
415 return -EINVAL;
416
417 if (val)
418 ret = stm_enable();
419 else
420 ret = stm_disable();
421
422 if (ret)
423 return ret;
Pratik Patela9c0e062012-05-28 13:45:35 -0700424 return size;
Pratik Patel5ecf6a12012-04-25 18:34:59 -0700425}
Pratik Patela9c0e062012-05-28 13:45:35 -0700426static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, stm_show_enabled,
427 stm_store_enabled);
428
429static ssize_t stm_show_entity(struct device *dev,
430 struct device_attribute *attr, char *buf)
Pratik Patel5ecf6a12012-04-25 18:34:59 -0700431{
Pratik Patela9c0e062012-05-28 13:45:35 -0700432 unsigned long val = stm.entity;
Pratik Patel5ecf6a12012-04-25 18:34:59 -0700433 return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
434}
Pratik Patel5ecf6a12012-04-25 18:34:59 -0700435
Pratik Patela9c0e062012-05-28 13:45:35 -0700436static ssize_t stm_store_entity(struct device *dev,
437 struct device_attribute *attr,
438 const char *buf, size_t size)
Pratik Patel5ecf6a12012-04-25 18:34:59 -0700439{
440 unsigned long val;
441
442 if (sscanf(buf, "%lx", &val) != 1)
443 return -EINVAL;
444
445 stm.entity = val;
Pratik Patela9c0e062012-05-28 13:45:35 -0700446 return size;
Pratik Patel5ecf6a12012-04-25 18:34:59 -0700447}
Pratik Patela9c0e062012-05-28 13:45:35 -0700448static DEVICE_ATTR(entity, S_IRUGO | S_IWUSR, stm_show_entity,
449 stm_store_entity);
Pratik Patel5ecf6a12012-04-25 18:34:59 -0700450
451static int __devinit stm_sysfs_init(void)
452{
453 int ret;
454
455 stm.kobj = kobject_create_and_add("stm", qdss_get_modulekobj());
456 if (!stm.kobj) {
457 dev_err(stm.dev, "failed to create STM sysfs kobject\n");
458 ret = -ENOMEM;
459 goto err_create;
460 }
461
Pratik Patela9c0e062012-05-28 13:45:35 -0700462 ret = sysfs_create_file(stm.kobj, &dev_attr_enabled.attr);
Pratik Patel5ecf6a12012-04-25 18:34:59 -0700463 if (ret) {
464 dev_err(stm.dev, "failed to create STM sysfs enabled attr\n");
465 goto err_file;
466 }
467
Pratik Patela9c0e062012-05-28 13:45:35 -0700468 if (sysfs_create_file(stm.kobj, &dev_attr_entity.attr))
Pratik Patel5ecf6a12012-04-25 18:34:59 -0700469 dev_err(stm.dev, "failed to create STM sysfs entity attr\n");
470
471 return 0;
472err_file:
473 kobject_put(stm.kobj);
474err_create:
475 return ret;
476}
477
478static void __devexit stm_sysfs_exit(void)
479{
Pratik Patela9c0e062012-05-28 13:45:35 -0700480 sysfs_remove_file(stm.kobj, &dev_attr_entity.attr);
481 sysfs_remove_file(stm.kobj, &dev_attr_enabled.attr);
Pratik Patel5ecf6a12012-04-25 18:34:59 -0700482 kobject_put(stm.kobj);
483}
484
485static int __devinit stm_probe(struct platform_device *pdev)
486{
487 int ret;
488 struct resource *res;
489 size_t res_size, bitmap_size;
490
491 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
492 if (!res) {
493 ret = -EINVAL;
494 goto err_res0;
495 }
496
497 stm.base = ioremap_nocache(res->start, resource_size(res));
498 if (!stm.base) {
499 ret = -EINVAL;
500 goto err_ioremap0;
501 }
502
503 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
504 if (!res) {
505 ret = -EINVAL;
506 goto err_res1;
507 }
508
509 if (stm_boot_nr_channel) {
510 res_size = min((resource_size_t)(stm_boot_nr_channel *
511 BYTES_PER_CHANNEL), resource_size(res));
512 bitmap_size = stm_boot_nr_channel * sizeof(long);
513 } else {
514 res_size = min((resource_size_t)(NR_STM_CHANNEL *
515 BYTES_PER_CHANNEL), resource_size(res));
516 bitmap_size = NR_STM_CHANNEL * sizeof(long);
517 }
518
519 stm.chs.bitmap = kzalloc(bitmap_size, GFP_KERNEL);
520 if (!stm.chs.bitmap) {
521 ret = -ENOMEM;
522 goto err_bitmap;
523 }
524
525 stm.chs.base = ioremap_nocache(res->start, res_size);
526 if (!stm.chs.base) {
527 ret = -EINVAL;
528 goto err_ioremap1;
529 }
530
531 stm.dev = &pdev->dev;
532
533 ret = misc_register(&stm_misc);
534 if (ret)
535 goto err_misc;
536
537 stm.src = qdss_get("msm_stm");
538 if (IS_ERR(stm.src)) {
539 ret = PTR_ERR(stm.src);
540 goto err_qdssget;
541 }
542
Pratik Patelf17b1472012-05-25 22:23:52 -0700543 stm.clk = clk_get(stm.dev, "core_clk");
544 if (IS_ERR(stm.clk)) {
545 ret = PTR_ERR(stm.clk);
546 goto err_clk_get;
547 }
548
549 ret = clk_set_rate(stm.clk, CS_CLK_RATE_TRACE);
550 if (ret)
551 goto err_clk_rate;
552
Pratik Patel5ecf6a12012-04-25 18:34:59 -0700553 ret = stm_sysfs_init();
554 if (ret)
555 goto err_sysfs;
556
557 if (stm_boot_enable)
558 stm_enable();
559
560 dev_info(stm.dev, "STM initialized\n");
561 return 0;
562
563err_sysfs:
Pratik Patelf17b1472012-05-25 22:23:52 -0700564err_clk_rate:
565 clk_put(stm.clk);
566err_clk_get:
Pratik Patel5ecf6a12012-04-25 18:34:59 -0700567 qdss_put(stm.src);
568err_qdssget:
569 misc_deregister(&stm_misc);
570err_misc:
571 iounmap(stm.chs.base);
572err_ioremap1:
573 kfree(stm.chs.bitmap);
574err_bitmap:
575err_res1:
576 iounmap(stm.base);
577err_ioremap0:
578err_res0:
579 dev_err(stm.dev, "STM init failed\n");
580 return ret;
581}
582
583static int __devexit stm_remove(struct platform_device *pdev)
584{
585 if (stm.enabled)
586 stm_disable();
587 stm_sysfs_exit();
Pratik Patelf17b1472012-05-25 22:23:52 -0700588 clk_put(stm.clk);
Pratik Patel5ecf6a12012-04-25 18:34:59 -0700589 qdss_put(stm.src);
590 misc_deregister(&stm_misc);
591 iounmap(stm.chs.base);
592 kfree(stm.chs.bitmap);
593 iounmap(stm.base);
594
595 return 0;
596}
597
Pratik Patel9eae4822012-05-14 17:34:53 -0700598static struct of_device_id stm_match[] = {
599 {.compatible = "qcom,msm-stm"},
600 {}
601};
602
Pratik Patel5ecf6a12012-04-25 18:34:59 -0700603static struct platform_driver stm_driver = {
604 .probe = stm_probe,
605 .remove = __devexit_p(stm_remove),
606 .driver = {
607 .name = "msm_stm",
Pratik Patel9eae4822012-05-14 17:34:53 -0700608 .owner = THIS_MODULE,
609 .of_match_table = stm_match,
Pratik Patel5ecf6a12012-04-25 18:34:59 -0700610 },
611};
612
613static int __init stm_init(void)
614{
615 return platform_driver_register(&stm_driver);
616}
617module_init(stm_init);
618
619static void __exit stm_exit(void)
620{
621 platform_driver_unregister(&stm_driver);
622}
623module_exit(stm_exit);
624
625MODULE_LICENSE("GPL v2");
626MODULE_DESCRIPTION("CoreSight System Trace Macrocell driver");