2 * arch/arm/mach-rockchip/last_log.c
4 * Copyright (C) 2011-2014 ROCKCHIP, Inc.
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.
11 #define pr_fmt(fmt) "last_log: " fmt
12 #include <linux/kernel.h>
13 #include <linux/init.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>
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;
27 static size_t log_pos;
28 static char early_log_buf[8192];
30 char *rk_last_log_get(unsigned *size)
36 static ssize_t last_log_read(struct file *file, char __user *buf,
37 size_t len, loff_t *offset)
42 if (pos >= LOG_BUF_LEN)
45 count = min(len, (size_t)(LOG_BUF_LEN - pos));
46 if (copy_to_user(buf, &last_log_buf[pos], count))
53 static const struct file_operations last_log_fops = {
55 .read = last_log_read,
58 static void * __init last_log_vmap(phys_addr_t start, unsigned int page_count)
60 struct page *pages[page_count + 1];
63 for (i = 0; i < page_count; i++) {
64 phys_addr_t addr = start + i * PAGE_SIZE;
65 pages[i] = pfn_to_page(addr >> PAGE_SHIFT);
67 pages[page_count] = pfn_to_page(start >> PAGE_SHIFT);
68 return vmap(pages, page_count + 1, VM_MAP, pgprot_noncached(PAGE_KERNEL));
71 static int __init rk_last_log_init(void)
73 size_t early_log_size;
75 struct proc_dir_entry *entry;
77 if (!cpu_is_rockchip())
80 buf = (char *)__get_free_pages(GFP_KERNEL, LOG_BUF_PAGE_ORDER);
82 pr_err("failed to __get_free_pages(%d)\n", LOG_BUF_PAGE_ORDER);
86 log_buf = last_log_vmap(virt_to_phys(buf), 1 << LOG_BUF_PAGE_ORDER);
88 pr_err("failed to map %d pages at 0x%08x\n", 1 << LOG_BUF_PAGE_ORDER, virt_to_phys(buf));
92 last_log_buf = (char *)vmalloc(LOG_BUF_LEN);
94 pr_err("failed to vmalloc(%d)\n", LOG_BUF_LEN);
98 memcpy(last_log_buf, buf, LOG_BUF_LEN);
99 early_log_size = log_pos > sizeof(early_log_buf) ? sizeof(early_log_buf) : log_pos;
100 memcpy(log_buf, early_log_buf, early_log_size);
101 memset(log_buf + early_log_size, 0, LOG_BUF_LEN - early_log_size);
103 pr_info("0x%08x map to 0x%p and copy to 0x%p, size 0x%x early 0x%x (version 3.0)\n", virt_to_phys(buf), log_buf, last_log_buf, LOG_BUF_LEN, early_log_size);
105 entry = proc_create("last_kmsg", S_IRUSR, NULL, &last_log_fops);
107 pr_err("failed to create proc entry\n");
110 proc_set_size(entry, LOG_BUF_LEN);
112 proc_symlink("last_log", NULL, "last_kmsg");
117 early_initcall(rk_last_log_init);
119 void rk_last_log_text(char *text, size_t size)
121 char *buf = log_buf ? log_buf : early_log_buf;
122 size_t log_size = log_buf ? LOG_BUF_LEN : sizeof(early_log_buf);
126 pos = log_pos & (log_size - 1);
127 if (likely(size + pos <= log_size))
128 memcpy(&buf[pos], text, size);
130 size_t first = log_size - pos;
131 size_t second = size - first;
132 memcpy(&buf[pos], text, first);
133 memcpy(&buf[0], text + first, second);