blob: db797cd9e323dbe3a4e8e6aa621b75be78a28ac3 [file] [log] [blame]
Mona Hossainf6efb8d2012-01-24 12:30:49 -08001/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
Mona Hossain11c03ac2011-10-26 12:42:10 -07002 *
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/debugfs.h>
14#include <linux/errno.h>
15#include <linux/io.h>
16#include <linux/kernel.h>
17#include <linux/module.h>
18#include <linux/platform_device.h>
19#include <linux/slab.h>
20#include <linux/types.h>
21#include <asm/uaccess.h>
22
23#define DEBUG_MAX_RW_BUF 4096
24
25/*
26 * Preprocessor Definitions and Constants
27 */
28#define TZBSP_CPU_COUNT 0x02
29/*
30 * Number of VMID Tables
31 */
32#define TZBSP_DIAG_NUM_OF_VMID 16
33/*
34 * VMID Description length
35 */
36#define TZBSP_DIAG_VMID_DESC_LEN 7
37/*
38 * Number of Interrupts
39 */
40#define TZBSP_DIAG_INT_NUM 32
41/*
42 * Length of descriptive name associated with Interrupt
43 */
44#define TZBSP_MAX_INT_DESC 16
45/*
46 * VMID Table
47 */
48struct tzdbg_vmid_t {
49 uint8_t vmid; /* Virtual Machine Identifier */
50 uint8_t desc[TZBSP_DIAG_VMID_DESC_LEN]; /* ASCII Text */
51};
52/*
53 * Boot Info Table
54 */
55struct tzdbg_boot_info_t {
Mona Hossainf6efb8d2012-01-24 12:30:49 -080056 uint32_t wb_entry_cnt; /* Warmboot entry CPU Counter */
57 uint32_t wb_exit_cnt; /* Warmboot exit CPU Counter */
58 uint32_t pc_entry_cnt; /* Power Collapse entry CPU Counter */
59 uint32_t pc_exit_cnt; /* Power Collapse exit CPU counter */
Mona Hossain11c03ac2011-10-26 12:42:10 -070060 uint32_t warm_jmp_addr; /* Last Warmboot Jump Address */
61 uint32_t spare; /* Reserved for future use. */
62};
63/*
64 * Reset Info Table
65 */
66struct tzdbg_reset_info_t {
67 uint32_t reset_type; /* Reset Reason */
68 uint32_t reset_cnt; /* Number of resets occured/CPU */
69};
70/*
71 * Interrupt Info Table
72 */
73struct tzdbg_int_t {
74 /*
75 * Type of Interrupt/exception
76 */
77 uint16_t int_info;
78 /*
79 * Availability of the slot
80 */
81 uint8_t avail;
82 /*
83 * Reserved for future use
84 */
85 uint8_t spare;
86 /*
87 * Interrupt # for IRQ and FIQ
88 */
89 uint32_t int_num;
90 /*
91 * ASCII text describing type of interrupt e.g:
92 * Secure Timer, EBI XPU. This string is always null terminated,
93 * supporting at most TZBSP_MAX_INT_DESC characters.
94 * Any additional characters are truncated.
95 */
96 uint8_t int_desc[TZBSP_MAX_INT_DESC];
97 uint64_t int_count[TZBSP_CPU_COUNT]; /* # of times seen per CPU */
98};
99/*
100 * Diagnostic Table
101 */
102struct tzdbg_t {
103 uint32_t magic_num;
104 uint32_t version;
105 /*
106 * Number of CPU's
107 */
108 uint32_t cpu_count;
109 /*
110 * Offset of VMID Table
111 */
112 uint32_t vmid_info_off;
113 /*
114 * Offset of Boot Table
115 */
116 uint32_t boot_info_off;
117 /*
118 * Offset of Reset info Table
119 */
120 uint32_t reset_info_off;
121 /*
122 * Offset of Interrupt info Table
123 */
124 uint32_t int_info_off;
125 /*
126 * Ring Buffer Offset
127 */
128 uint32_t ring_off;
129 /*
130 * Ring Buffer Length
131 */
132 uint32_t ring_len;
133 /*
134 * VMID to EE Mapping
135 */
136 struct tzdbg_vmid_t vmid_info[TZBSP_DIAG_NUM_OF_VMID];
137 /*
138 * Boot Info
139 */
140 struct tzdbg_boot_info_t boot_info[TZBSP_CPU_COUNT];
141 /*
142 * Reset Info
143 */
144 struct tzdbg_reset_info_t reset_info[TZBSP_CPU_COUNT];
145 uint32_t num_interrupts;
146 struct tzdbg_int_t int_info[TZBSP_DIAG_INT_NUM];
147 /*
148 * We need at least 2K for the ring buffer
149 */
150 uint8_t *ring_buffer; /* TZ Ring Buffer */
151};
152
153/*
154 * Enumeration order for VMID's
155 */
156enum tzdbg_stats_type {
157 TZDBG_BOOT = 0,
158 TZDBG_RESET,
159 TZDBG_INTERRUPT,
160 TZDBG_VMID,
161 TZDBG_GENERAL,
162 TZDBG_LOG,
163 TZDBG_STATS_MAX,
164};
165
166struct tzdbg_stat {
167 char *name;
168 char *data;
169};
170
171struct tzdbg {
172 void __iomem *virt_iobase;
173 struct tzdbg_t *diag_buf;
174 char *disp_buf;
175 int debug_tz[TZDBG_STATS_MAX];
176 struct tzdbg_stat stat[TZDBG_STATS_MAX];
177};
178
179static struct tzdbg tzdbg = {
180
181 .stat[TZDBG_BOOT].name = "boot",
182 .stat[TZDBG_RESET].name = "reset",
183 .stat[TZDBG_INTERRUPT].name = "interrupt",
184 .stat[TZDBG_VMID].name = "vmid",
185 .stat[TZDBG_GENERAL].name = "general",
186 .stat[TZDBG_LOG].name = "log",
187};
188
189
190/*
191 * Debugfs data structure and functions
192 */
193
194static int _disp_tz_general_stats(void)
195{
196 int len = 0;
197
198 len += snprintf(tzdbg.disp_buf + len, DEBUG_MAX_RW_BUF - 1,
199 " Version : 0x%x\n"
200 " Magic Number : 0x%x\n"
201 " Number of CPU : %d\n",
202 tzdbg.diag_buf->version,
203 tzdbg.diag_buf->magic_num,
204 tzdbg.diag_buf->cpu_count);
205 tzdbg.stat[TZDBG_GENERAL].data = tzdbg.disp_buf;
206 return len;
207}
208
209static int _disp_tz_vmid_stats(void)
210{
211 int i, num_vmid;
212 int len = 0;
213 struct tzdbg_vmid_t *ptr;
214
215 ptr = (struct tzdbg_vmid_t *)((unsigned char *)tzdbg.diag_buf +
216 tzdbg.diag_buf->vmid_info_off);
217 num_vmid = ((tzdbg.diag_buf->boot_info_off -
218 tzdbg.diag_buf->vmid_info_off)/
219 (sizeof(struct tzdbg_vmid_t)));
220
221 for (i = 0; i < num_vmid; i++) {
222 if (ptr->vmid < 0xFF) {
223 len += snprintf(tzdbg.disp_buf + len,
224 (DEBUG_MAX_RW_BUF - 1) - len,
225 " 0x%x %s\n",
226 (uint32_t)ptr->vmid, (uint8_t *)ptr->desc);
227 }
228 if (len > (DEBUG_MAX_RW_BUF - 1)) {
229 pr_warn("%s: Cannot fit all info into the buffer\n",
230 __func__);
231 break;
232 }
233 ptr++;
234 }
235
236 tzdbg.stat[TZDBG_VMID].data = tzdbg.disp_buf;
237 return len;
238}
239
240static int _disp_tz_boot_stats(void)
241{
242 int i;
243 int len = 0;
244 struct tzdbg_boot_info_t *ptr;
245
246 ptr = (struct tzdbg_boot_info_t *)((unsigned char *)tzdbg.diag_buf +
247 tzdbg.diag_buf->boot_info_off);
248
249 for (i = 0; i < tzdbg.diag_buf->cpu_count; i++) {
250 len += snprintf(tzdbg.disp_buf + len,
251 (DEBUG_MAX_RW_BUF - 1) - len,
252 " CPU #: %d\n"
253 " Warmboot jump address : 0x%x\n"
254 " Warmboot entry CPU counter: 0x%x\n"
Mona Hossainf6efb8d2012-01-24 12:30:49 -0800255 " Warmboot exit CPU counter : 0x%x\n"
256 " Power Collapse entry CPU counter: 0x%x\n"
257 " Power Collapse exit CPU counter : 0x%x\n",
258 i, ptr->warm_jmp_addr, ptr->wb_entry_cnt,
259 ptr->wb_exit_cnt, ptr->pc_entry_cnt,
260 ptr->pc_exit_cnt);
Mona Hossain11c03ac2011-10-26 12:42:10 -0700261
262 if (len > (DEBUG_MAX_RW_BUF - 1)) {
263 pr_warn("%s: Cannot fit all info into the buffer\n",
264 __func__);
265 break;
266 }
267 ptr++;
268 }
269 tzdbg.stat[TZDBG_BOOT].data = tzdbg.disp_buf;
270 return len;
271}
272
273static int _disp_tz_reset_stats(void)
274{
275 int i;
276 int len = 0;
277 struct tzdbg_reset_info_t *ptr;
278
279 ptr = (struct tzdbg_reset_info_t *)((unsigned char *)tzdbg.diag_buf +
280 tzdbg.diag_buf->reset_info_off);
281
282 for (i = 0; i < tzdbg.diag_buf->cpu_count; i++) {
283 len += snprintf(tzdbg.disp_buf + len,
284 (DEBUG_MAX_RW_BUF - 1) - len,
285 " CPU #: %d\n"
286 " Reset Type (reason) : 0x%x\n"
287 " Reset counter : 0x%x\n",
288 i, ptr->reset_type, ptr->reset_cnt);
289
290 if (len > (DEBUG_MAX_RW_BUF - 1)) {
291 pr_warn("%s: Cannot fit all info into the buffer\n",
292 __func__);
293 break;
294 }
295
296 ptr++;
297 }
298 tzdbg.stat[TZDBG_RESET].data = tzdbg.disp_buf;
299 return len;
300}
301
302static int _disp_tz_interrupt_stats(void)
303{
304 int i, j, int_info_size;
305 int len = 0;
306 int *num_int;
307 unsigned char *ptr;
308 struct tzdbg_int_t *tzdbg_ptr;
309
310 num_int = (uint32_t *)((unsigned char *)tzdbg.diag_buf +
311 (tzdbg.diag_buf->int_info_off - sizeof(uint32_t)));
312 ptr = ((unsigned char *)tzdbg.diag_buf +
313 tzdbg.diag_buf->int_info_off);
314 int_info_size = ((tzdbg.diag_buf->ring_off -
315 tzdbg.diag_buf->int_info_off)/(*num_int));
316
317 for (i = 0; i < (*num_int); i++) {
318 tzdbg_ptr = (struct tzdbg_int_t *)ptr;
319 len += snprintf(tzdbg.disp_buf + len,
320 (DEBUG_MAX_RW_BUF - 1) - len,
321 " Interrupt Number : 0x%x\n"
322 " Type of Interrupt : 0x%x\n"
323 " Description of interrupt : %s\n",
324 tzdbg_ptr->int_num,
325 (uint32_t)tzdbg_ptr->int_info,
326 (uint8_t *)tzdbg_ptr->int_desc);
327 for (j = 0; j < tzdbg.diag_buf->cpu_count; j++) {
328 len += snprintf(tzdbg.disp_buf + len,
329 (DEBUG_MAX_RW_BUF - 1) - len,
330 " int_count on CPU # %d : %u\n",
331 (uint32_t)j,
332 (uint32_t)tzdbg_ptr->int_count[j]);
333 }
334 len += snprintf(tzdbg.disp_buf + len, DEBUG_MAX_RW_BUF - 1,
335 "\n");
336
337 if (len > (DEBUG_MAX_RW_BUF - 1)) {
338 pr_warn("%s: Cannot fit all info into the buffer\n",
339 __func__);
340 break;
341 }
342
343 ptr += int_info_size;
344 }
345 tzdbg.stat[TZDBG_INTERRUPT].data = tzdbg.disp_buf;
346 return len;
347}
348
349static int _disp_tz_log_stats(void)
350{
351 int len = 0;
352 unsigned char *ptr;
353
354 ptr = (unsigned char *)tzdbg.diag_buf +
355 tzdbg.diag_buf->ring_off;
356 len += snprintf(tzdbg.disp_buf, (DEBUG_MAX_RW_BUF - 1) - len,
357 "%s\n", ptr);
358
359 tzdbg.stat[TZDBG_LOG].data = tzdbg.disp_buf;
360 return len;
361}
362
363static ssize_t tzdbgfs_read(struct file *file, char __user *buf,
364 size_t count, loff_t *offp)
365{
366 int len = 0;
367 int *tz_id = file->private_data;
368
369 memcpy_fromio((void *)tzdbg.diag_buf, tzdbg.virt_iobase,
370 DEBUG_MAX_RW_BUF);
371 switch (*tz_id) {
372 case TZDBG_BOOT:
373 len = _disp_tz_boot_stats();
374 break;
375 case TZDBG_RESET:
376 len = _disp_tz_reset_stats();
377 break;
378 case TZDBG_INTERRUPT:
379 len = _disp_tz_interrupt_stats();
380 break;
381 case TZDBG_GENERAL:
382 len = _disp_tz_general_stats();
383 break;
384 case TZDBG_VMID:
385 len = _disp_tz_vmid_stats();
386 break;
387 case TZDBG_LOG:
388 len = _disp_tz_log_stats();
389 break;
390 default:
391 break;
392 }
393
394 if (len > count)
395 len = count;
396
397 return simple_read_from_buffer(buf, len, offp,
398 tzdbg.stat[(*tz_id)].data, len);
399}
400
401static int tzdbgfs_open(struct inode *inode, struct file *pfile)
402{
403 pfile->private_data = inode->i_private;
404 return 0;
405}
406
407const struct file_operations tzdbg_fops = {
408 .owner = THIS_MODULE,
409 .read = tzdbgfs_read,
410 .open = tzdbgfs_open,
411};
412
413static int tzdbgfs_init(struct platform_device *pdev)
414{
415 int rc = 0;
416 int i;
417 struct dentry *dent_dir;
418 struct dentry *dent;
419
420 dent_dir = debugfs_create_dir("tzdbg", NULL);
421 if (dent_dir == NULL) {
422 dev_err(&pdev->dev, "tzdbg debugfs_create_dir failed\n");
423 return -ENOMEM;
424 }
425
426 for (i = 0; i < TZDBG_STATS_MAX; i++) {
427 tzdbg.debug_tz[i] = i;
428 dent = debugfs_create_file(tzdbg.stat[i].name,
429 S_IRUGO, dent_dir,
430 &tzdbg.debug_tz[i], &tzdbg_fops);
431 if (dent == NULL) {
432 dev_err(&pdev->dev, "TZ debugfs_create_file failed\n");
433 rc = -ENOMEM;
434 goto err;
435 }
436 }
437 tzdbg.disp_buf = kzalloc(DEBUG_MAX_RW_BUF, GFP_KERNEL);
438 if (tzdbg.disp_buf == NULL) {
439 pr_err("%s: Can't Allocate memory for tzdbg.disp_buf\n",
440 __func__);
441
442 goto err;
443 }
444 platform_set_drvdata(pdev, dent_dir);
445 return 0;
446err:
447 debugfs_remove_recursive(dent_dir);
448
449 return rc;
450}
451
452static void tzdbgfs_exit(struct platform_device *pdev)
453{
454 struct dentry *dent_dir;
455
456 kzfree(tzdbg.disp_buf);
457 dent_dir = platform_get_drvdata(pdev);
458 debugfs_remove_recursive(dent_dir);
459}
460
461/*
462 * Driver functions
463 */
464static int __devinit tz_log_probe(struct platform_device *pdev)
465{
466 struct resource *resource;
467 void __iomem *virt_iobase;
468 uint32_t tzdiag_phy_iobase;
469 uint32_t *ptr = NULL;
470
471 /*
472 * Get address that stores the physical location of 4KB
473 * diagnostic data
474 */
475 resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
476 if (!resource) {
477 dev_err(&pdev->dev,
478 "%s: ERROR Missing MEM resource\n", __func__);
479 return -ENXIO;
480 };
481 /*
482 * Map address that stores the physical location of 4KB
483 * diagnostic data
484 */
485 virt_iobase = devm_ioremap_nocache(&pdev->dev, resource->start,
486 resource->end - resource->start + 1);
487 if (!virt_iobase) {
488 dev_err(&pdev->dev,
489 "%s: ERROR could not ioremap: start=%p, len=%u\n",
490 __func__, (void *) resource->start,
491 (resource->end - resource->start + 1));
492 return -ENXIO;
493 }
494 /*
495 * Retrieve the address of 4KB diagnostic data
496 */
497 tzdiag_phy_iobase = readl_relaxed(virt_iobase);
498
499 /*
500 * Map the 4KB diagnostic information area
501 */
502 tzdbg.virt_iobase = devm_ioremap_nocache(&pdev->dev,
503 tzdiag_phy_iobase, DEBUG_MAX_RW_BUF);
504
505 if (!tzdbg.virt_iobase) {
506 dev_err(&pdev->dev,
507 "%s: ERROR could not ioremap: start=%p, len=%u\n",
508 __func__, (void *) tzdiag_phy_iobase, DEBUG_MAX_RW_BUF);
509 return -ENXIO;
510 }
511
512 ptr = kzalloc(DEBUG_MAX_RW_BUF, GFP_KERNEL);
513 if (ptr == NULL) {
514 pr_err("%s: Can't Allocate memory: ptr\n",
515 __func__);
516 return -ENXIO;
517 }
518
519 tzdbg.diag_buf = (struct tzdbg_t *)ptr;
520
521 if (tzdbgfs_init(pdev))
522 goto err;
523
524 return 0;
525err:
526 kfree(tzdbg.diag_buf);
527 return -ENXIO;
528}
529
530
531static int __devexit tz_log_remove(struct platform_device *pdev)
532{
533 kzfree(tzdbg.diag_buf);
534 tzdbgfs_exit(pdev);
535
536 return 0;
537}
538
Hariprasad Dhalinarasimha71b536f2012-06-28 17:39:35 -0700539static struct of_device_id tzlog_match[] = {
540 { .compatible = "qcom,tz-log",
541 },
542 {}
543};
544
Mona Hossain11c03ac2011-10-26 12:42:10 -0700545static struct platform_driver tz_log_driver = {
546 .probe = tz_log_probe,
547 .remove = __devexit_p(tz_log_remove),
548 .driver = {
549 .name = "tz_log",
550 .owner = THIS_MODULE,
Hariprasad Dhalinarasimha71b536f2012-06-28 17:39:35 -0700551 .of_match_table = tzlog_match,
Mona Hossain11c03ac2011-10-26 12:42:10 -0700552 },
553};
554
555static int __init tz_log_init(void)
556{
557 return platform_driver_register(&tz_log_driver);
558}
559
560static void __exit tz_log_exit(void)
561{
562 platform_driver_unregister(&tz_log_driver);
563}
564
565module_init(tz_log_init);
566module_exit(tz_log_exit);
567
568MODULE_LICENSE("GPL v2");
569MODULE_DESCRIPTION("TZ Log driver");
Mona Hossainf6efb8d2012-01-24 12:30:49 -0800570MODULE_VERSION("1.1");
Mona Hossain11c03ac2011-10-26 12:42:10 -0700571MODULE_ALIAS("platform:tz_log");