From cd55a91334d3b8e4f5c59c027b6be2c9cd3cf056 Mon Sep 17 00:00:00 2001
From: =?utf8?q?=E9=BB=84=E6=B6=9B?= <huangtao@rock-chips.com>
Date: Tue, 1 Apr 2014 10:55:45 +0800
Subject: [PATCH] ARM: rockchip: add last_log support

---
 arch/arm/mach-rockchip/last_log.c | 104 ++++++++++++++++++++++++++++++
 1 file changed, 104 insertions(+)
 create mode 100644 arch/arm/mach-rockchip/last_log.c

diff --git a/arch/arm/mach-rockchip/last_log.c b/arch/arm/mach-rockchip/last_log.c
new file mode 100644
index 000000000000..23c387d9675d
--- /dev/null
+++ b/arch/arm/mach-rockchip/last_log.c
@@ -0,0 +1,104 @@
+/*
+ *  arch/arm/mach-rockchip/last_log.c
+ *
+ *  Copyright (C) 2011-2014 ROCKCHIP, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) "last_log: " fmt
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/vmalloc.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#define LOG_BUF_LEN	(1 << CONFIG_LOG_BUF_SHIFT)
+#define LOG_BUF_PAGE_ORDER	(CONFIG_LOG_BUF_SHIFT - PAGE_SHIFT)
+static char last_log_buf[LOG_BUF_LEN];
+extern void __init switch_log_buf(char *new_log_buf, unsigned size);
+
+char *last_log_get(unsigned *size)
+{
+	*size = LOG_BUF_LEN;
+	return last_log_buf;
+}
+
+static ssize_t last_log_read(struct file *file, char __user *buf,
+				    size_t len, loff_t *offset)
+{
+	loff_t pos = *offset;
+	ssize_t count;
+
+	if (pos >= LOG_BUF_LEN)
+		return 0;
+
+	count = min(len, (size_t)(LOG_BUF_LEN - pos));
+	if (copy_to_user(buf, &last_log_buf[pos], count))
+		return -EFAULT;
+
+	*offset += count;
+	return count;
+}
+
+static const struct file_operations last_log_fops = {
+	.owner = THIS_MODULE,
+	.read = last_log_read,
+};
+
+static void * __init last_log_vmap(phys_addr_t start, unsigned int page_count)
+{
+	struct page *pages[page_count + 1];
+	unsigned int i;
+
+	for (i = 0; i < page_count; i++) {
+		phys_addr_t addr = start + i * PAGE_SIZE;
+		pages[i] = pfn_to_page(addr >> PAGE_SHIFT);
+	}
+	pages[page_count] = pfn_to_page(start >> PAGE_SHIFT);
+	return vmap(pages, page_count + 1, VM_MAP, pgprot_noncached(PAGE_KERNEL));
+}
+
+static int __init last_log_init(void)
+{
+	char *log_buf, *new_log_buf;
+	struct proc_dir_entry *entry;
+
+	log_buf = (char *)__get_free_pages(GFP_KERNEL, LOG_BUF_PAGE_ORDER);
+	if (!log_buf) {
+		pr_err("failed to __get_free_pages(%d)\n", LOG_BUF_PAGE_ORDER);
+		return 0;
+	}
+
+	new_log_buf = last_log_vmap(virt_to_phys(log_buf), 1 << LOG_BUF_PAGE_ORDER);
+	if (!new_log_buf) {
+		pr_err("failed to map %d pages at 0x%08x\n", 1 << LOG_BUF_PAGE_ORDER, virt_to_phys(log_buf));
+		return 0;
+	}
+
+	pr_info("0x%08x map to 0x%p and copy to 0x%p (version 2.2)\n", virt_to_phys(log_buf), new_log_buf, last_log_buf);
+
+	memcpy(last_log_buf, new_log_buf, LOG_BUF_LEN);
+	memset(new_log_buf, 0, LOG_BUF_LEN);
+	switch_log_buf(new_log_buf, LOG_BUF_LEN);
+
+	entry = proc_create("last_log", S_IRUSR, NULL, &last_log_fops);
+	if (!entry) {
+		pr_err("failed to create proc entry\n");
+		return 0;
+	}
+	proc_set_size(entry, LOG_BUF_LEN);
+
+#ifndef CONFIG_ANDROID_RAM_CONSOLE
+	proc_symlink("last_kmsg", NULL, "last_log");
+#endif
+
+	return 0;
+}
+
+postcore_initcall(last_log_init);
-- 
2.34.1