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