arm64: configs: synchronize with other 3399 config for 3399 linux
[firefly-linux-kernel-4.4.55.git] / arch / arm / mach-rockchip / last_log.c
1 /*
2  *  arch/arm/mach-rockchip/last_log.c
3  *
4  *  Copyright (C) 2011-2014 ROCKCHIP, Inc.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10
11 #define pr_fmt(fmt) "last_log: " fmt
12 #include <linux/kernel.h>
13 #include <linux/init.h>
14 #include <linux/mm.h>
15 #include <linux/module.h>
16 #include <linux/proc_fs.h>
17 #include <linux/vmalloc.h>
18 #include <linux/rockchip/cpu.h>
19 #include <asm/uaccess.h>
20 #include <asm/io.h>
21
22 #define LOG_BUF_SHIFT   CONFIG_LOG_BUF_SHIFT
23 #define LOG_BUF_LEN     (1 << LOG_BUF_SHIFT)
24 #define LOG_BUF_PAGE_ORDER      (LOG_BUF_SHIFT - PAGE_SHIFT)
25 static char *last_log_buf;
26 static char *log_buf;
27 static size_t log_pos;
28 static char early_log_buf[8192];
29
30 char *rk_last_log_get(unsigned *size)
31 {
32         *size = LOG_BUF_LEN;
33         return last_log_buf;
34 }
35
36 static ssize_t last_log_read(struct file *file, char __user *buf,
37                                     size_t len, loff_t *offset)
38 {
39         loff_t pos = *offset;
40         ssize_t count;
41
42         if (pos >= LOG_BUF_LEN)
43                 return 0;
44
45         count = min(len, (size_t)(LOG_BUF_LEN - pos));
46         if (copy_to_user(buf, &last_log_buf[pos], count))
47                 return -EFAULT;
48
49         *offset += count;
50         return count;
51 }
52
53 static const struct file_operations last_log_fops = {
54         .owner = THIS_MODULE,
55         .read = last_log_read,
56 };
57
58 static void * __init last_log_vmap(phys_addr_t start, unsigned int page_count)
59 {
60         struct page *pages[page_count + 1];
61         unsigned int i;
62         pgprot_t prot;
63
64         for (i = 0; i < page_count; i++) {
65                 phys_addr_t addr = start + i * PAGE_SIZE;
66                 pages[i] = pfn_to_page(addr >> PAGE_SHIFT);
67         }
68         pages[page_count] = pfn_to_page(start >> PAGE_SHIFT);
69 #ifdef CONFIG_ARM64
70         prot = pgprot_writecombine(PAGE_KERNEL);
71 #else
72         prot = pgprot_noncached(PAGE_KERNEL);
73 #endif
74         return vmap(pages, page_count + 1, VM_MAP, prot);
75 }
76
77 static int __init rk_last_log_init(void)
78 {
79         size_t early_log_size;
80         char *buf;
81         struct proc_dir_entry *entry;
82         phys_addr_t buf_phys;
83
84         buf = (char *)__get_free_pages(GFP_KERNEL, LOG_BUF_PAGE_ORDER);
85         if (!buf) {
86                 pr_err("failed to __get_free_pages(%d)\n", LOG_BUF_PAGE_ORDER);
87                 return 0;
88         }
89         buf_phys = virt_to_phys(buf);
90
91         log_buf = last_log_vmap(buf_phys, 1 << LOG_BUF_PAGE_ORDER);
92         if (!log_buf) {
93                 pr_err("failed to map %d pages at %pa\n", 1 << LOG_BUF_PAGE_ORDER,
94                        &buf_phys);
95                 return 0;
96         }
97
98         last_log_buf = (char *)vmalloc(LOG_BUF_LEN);
99         if (!last_log_buf) {
100                 pr_err("failed to vmalloc(%d)\n", LOG_BUF_LEN);
101                 return 0;
102         }
103
104         memcpy(last_log_buf, buf, LOG_BUF_LEN);
105         early_log_size = log_pos > sizeof(early_log_buf) ? sizeof(early_log_buf) : log_pos;
106         memcpy(log_buf, early_log_buf, early_log_size);
107         memset(log_buf + early_log_size, 0, LOG_BUF_LEN - early_log_size);
108
109         pr_info("%pa map to 0x%p and copy to 0x%p, size 0x%x early 0x%zx (version 3.1)\n", &buf_phys, log_buf, last_log_buf, LOG_BUF_LEN, early_log_size);
110
111         entry = proc_create("last_kmsg", S_IRUSR, NULL, &last_log_fops);
112         if (!entry) {
113                 pr_err("failed to create proc entry\n");
114                 return 0;
115         }
116         proc_set_size(entry, LOG_BUF_LEN);
117
118         proc_symlink("last_log", NULL, "last_kmsg");
119
120         return 0;
121 }
122
123 early_initcall(rk_last_log_init);
124
125 void rk_last_log_text(char *text, size_t size)
126 {
127         char *buf = log_buf ? log_buf : early_log_buf;
128         size_t log_size = log_buf ? LOG_BUF_LEN : sizeof(early_log_buf);
129         size_t pos;
130
131         /* Check overflow */
132         pos = log_pos & (log_size - 1);
133         if (likely(size + pos <= log_size))
134                 memcpy(&buf[pos], text, size);
135         else {
136                 size_t first = log_size - pos;
137                 size_t second = size - first;
138                 memcpy(&buf[pos], text, first);
139                 memcpy(&buf[0], text + first, second);
140         }
141
142         log_pos += size;
143 }