aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
authorcodeworkx <daniel.hillenbrand@codeworkx.de>2012-06-02 13:09:29 +0200
committercodeworkx <daniel.hillenbrand@codeworkx.de>2012-06-02 13:09:29 +0200
commitc6da2cfeb05178a11c6d062a06f8078150ee492f (patch)
treef3b4021d252c52d6463a9b3c1bb7245e399b009c /drivers/char
parentc6d7c4dbff353eac7919342ae6b3299a378160a6 (diff)
downloadkernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.zip
kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.gz
kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.bz2
samsung update 1
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/Kconfig47
-rw-r--r--drivers/char/Makefile4
-rw-r--r--drivers/char/dcc_tty.c326
-rw-r--r--drivers/char/exynos_mem.c286
-rw-r--r--drivers/char/mem.c117
-rw-r--r--drivers/char/s3c_mem.c777
-rw-r--r--drivers/char/s3c_mem.h140
7 files changed, 1696 insertions, 1 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 49502bc..ef176ae 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -6,6 +6,19 @@ menu "Character devices"
source "drivers/tty/Kconfig"
+config DEVMEM
+ bool "Memory device driver"
+ default y
+ help
+ The memory driver provides two character devices, mem and kmem, which
+ provide access to the system's memory. The mem device is a view of
+ physical memory, and each byte in the device corresponds to the
+ matching physical address. The kmem device is the same as mem, but
+ the addresses correspond to the kernel's virtual address space rather
+ than physical memory. These devices are standard parts of a Linux
+ system and most users should say Y here. You might say N if very
+ security conscience or memory is tight.
+
config DEVKMEM
bool "/dev/kmem virtual device support"
default y
@@ -598,6 +611,10 @@ config DEVPORT
depends on ISA || PCI
default y
+config DCC_TTY
+ tristate "DCC tty driver"
+ depends on ARM
+
source "drivers/s390/char/Kconfig"
config RAMOOPS
@@ -608,6 +625,36 @@ config RAMOOPS
This enables panic and oops messages to be logged to a circular
buffer in RAM where it can be read back at some later point.
+config S3C_MEM
+ bool "Support for /dev/s3c-mem"
+ default y
+ ---help---
+ If you do say Y here, you can allocate physically linear memories from system memory.
+ And you can share the memory at the other process using re-allocation ioctl.
+
+ If unsure, say Y.
+
+config S3C_MEM_CMA_ALLOC
+ bool "Support for /dev/s3c-mem with CMA feature"
+ depends on S3C_MEM && SLP
+
+config VIDEO_SAMSUNG_MEMSIZE_S3C_MEM_CMA
+ int "Memory size in kbytes for S3C_MEM_CMA"
+ depends on S3C_MEM_CMA_ALLOC
+ default "24576"
+
+config VIDEO_SAMSUNG_SLOTSIZE_S3C_MEM_CMA
+ int "Slot size in kbytes for S3C_MEM_CMA slot"
+ depends on S3C_MEM_CMA_ALLOC
+ default "1024"
+
+config EXYNOS_MEM
+ bool "Support for /dev/exynos-mem"
+ default y
+ help
+ If you do say Y here, you can mmap using physically linear memories.
+ And you can flush it using ioctl.
+
config MSM_SMD_PKT
bool "Enable device interface for some SMD packet ports"
default n
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 7a00672..4383feb 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -58,8 +58,12 @@ obj-$(CONFIG_IPMI_HANDLER) += ipmi/
obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
obj-$(CONFIG_TCG_TPM) += tpm/
+obj-$(CONFIG_DCC_TTY) += dcc_tty.o
obj-$(CONFIG_PS3_FLASH) += ps3flash.o
obj-$(CONFIG_RAMOOPS) += ramoops.o
obj-$(CONFIG_JS_RTC) += js-rtc.o
js-rtc-y = rtc.o
+
+obj-$(CONFIG_S3C_MEM) += s3c_mem.o
+obj-$(CONFIG_EXYNOS_MEM) += exynos_mem.o
diff --git a/drivers/char/dcc_tty.c b/drivers/char/dcc_tty.c
new file mode 100644
index 0000000..a787acc
--- /dev/null
+++ b/drivers/char/dcc_tty.c
@@ -0,0 +1,326 @@
+/* drivers/char/dcc_tty.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/console.h>
+#include <linux/hrtimer.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+
+MODULE_DESCRIPTION("DCC TTY Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+
+static spinlock_t g_dcc_tty_lock = SPIN_LOCK_UNLOCKED;
+static struct hrtimer g_dcc_timer;
+static char g_dcc_buffer[16];
+static int g_dcc_buffer_head;
+static int g_dcc_buffer_count;
+static unsigned g_dcc_write_delay_usecs = 1;
+static struct tty_driver *g_dcc_tty_driver;
+static struct tty_struct *g_dcc_tty;
+static int g_dcc_tty_open_count;
+
+static void dcc_poll_locked(void)
+{
+ char ch;
+ int rch;
+ int written;
+
+ while (g_dcc_buffer_count) {
+ ch = g_dcc_buffer[g_dcc_buffer_head];
+ asm(
+ "mrc 14, 0, r15, c0, c1, 0\n"
+ "mcrcc 14, 0, %1, c0, c5, 0\n"
+ "movcc %0, #1\n"
+ "movcs %0, #0\n"
+ : "=r" (written)
+ : "r" (ch)
+ );
+ if (written) {
+ if (ch == '\n')
+ g_dcc_buffer[g_dcc_buffer_head] = '\r';
+ else {
+ g_dcc_buffer_head = (g_dcc_buffer_head + 1) % ARRAY_SIZE(g_dcc_buffer);
+ g_dcc_buffer_count--;
+ if (g_dcc_tty)
+ tty_wakeup(g_dcc_tty);
+ }
+ g_dcc_write_delay_usecs = 1;
+ } else {
+ if (g_dcc_write_delay_usecs > 0x100)
+ break;
+ g_dcc_write_delay_usecs <<= 1;
+ udelay(g_dcc_write_delay_usecs);
+ }
+ }
+
+ if (g_dcc_tty && !test_bit(TTY_THROTTLED, &g_dcc_tty->flags)) {
+ asm(
+ "mrc 14, 0, %0, c0, c1, 0\n"
+ "tst %0, #(1 << 30)\n"
+ "moveq %0, #-1\n"
+ "mrcne 14, 0, %0, c0, c5, 0\n"
+ : "=r" (rch)
+ );
+ if (rch >= 0) {
+ ch = rch;
+ tty_insert_flip_string(g_dcc_tty, &ch, 1);
+ tty_flip_buffer_push(g_dcc_tty);
+ }
+ }
+
+
+ if (g_dcc_buffer_count)
+ hrtimer_start(&g_dcc_timer, ktime_set(0, g_dcc_write_delay_usecs * NSEC_PER_USEC), HRTIMER_MODE_REL);
+ else
+ hrtimer_start(&g_dcc_timer, ktime_set(0, 20 * NSEC_PER_MSEC), HRTIMER_MODE_REL);
+}
+
+static int dcc_tty_open(struct tty_struct * tty, struct file * filp)
+{
+ int ret;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
+ if (g_dcc_tty == NULL || g_dcc_tty == tty) {
+ g_dcc_tty = tty;
+ g_dcc_tty_open_count++;
+ ret = 0;
+ } else
+ ret = -EBUSY;
+ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
+
+ printk("dcc_tty_open, tty %p, f_flags %x, returned %d\n", tty, filp->f_flags, ret);
+
+ return ret;
+}
+
+static void dcc_tty_close(struct tty_struct * tty, struct file * filp)
+{
+ printk("dcc_tty_close, tty %p, f_flags %x\n", tty, filp->f_flags);
+ if (g_dcc_tty == tty) {
+ if (--g_dcc_tty_open_count == 0)
+ g_dcc_tty = NULL;
+ }
+}
+
+static int dcc_write(const unsigned char *buf_start, int count)
+{
+ const unsigned char *buf = buf_start;
+ unsigned long irq_flags;
+ int copy_len;
+ int space_left;
+ int tail;
+
+ if (count < 1)
+ return 0;
+
+ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
+ do {
+ tail = (g_dcc_buffer_head + g_dcc_buffer_count) % ARRAY_SIZE(g_dcc_buffer);
+ copy_len = ARRAY_SIZE(g_dcc_buffer) - tail;
+ space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count;
+ if (copy_len > space_left)
+ copy_len = space_left;
+ if (copy_len > count)
+ copy_len = count;
+ memcpy(&g_dcc_buffer[tail], buf, copy_len);
+ g_dcc_buffer_count += copy_len;
+ buf += copy_len;
+ count -= copy_len;
+ if (copy_len < count && copy_len < space_left) {
+ space_left -= copy_len;
+ copy_len = count;
+ if (copy_len > space_left) {
+ copy_len = space_left;
+ }
+ memcpy(g_dcc_buffer, buf, copy_len);
+ buf += copy_len;
+ count -= copy_len;
+ g_dcc_buffer_count += copy_len;
+ }
+ dcc_poll_locked();
+ space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count;
+ } while(count && space_left);
+ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
+ return buf - buf_start;
+}
+
+static int dcc_tty_write(struct tty_struct * tty, const unsigned char *buf, int count)
+{
+ int ret;
+ /* printk("dcc_tty_write %p, %d\n", buf, count); */
+ ret = dcc_write(buf, count);
+ if (ret != count)
+ printk("dcc_tty_write %p, %d, returned %d\n", buf, count, ret);
+ return ret;
+}
+
+static int dcc_tty_write_room(struct tty_struct *tty)
+{
+ int space_left;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
+ space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count;
+ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
+ return space_left;
+}
+
+static int dcc_tty_chars_in_buffer(struct tty_struct *tty)
+{
+ int ret;
+ asm(
+ "mrc 14, 0, %0, c0, c1, 0\n"
+ "mov %0, %0, LSR #30\n"
+ "and %0, %0, #1\n"
+ : "=r" (ret)
+ );
+ return ret;
+}
+
+static void dcc_tty_unthrottle(struct tty_struct * tty)
+{
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
+ dcc_poll_locked();
+ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
+}
+
+static enum hrtimer_restart dcc_tty_timer_func(struct hrtimer *timer)
+{
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
+ dcc_poll_locked();
+ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
+ return HRTIMER_NORESTART;
+}
+
+void dcc_console_write(struct console *co, const char *b, unsigned count)
+{
+#if 1
+ dcc_write(b, count);
+#else
+ /* blocking printk */
+ while (count > 0) {
+ int written;
+ written = dcc_write(b, count);
+ if (written) {
+ b += written;
+ count -= written;
+ }
+ }
+#endif
+}
+
+static struct tty_driver *dcc_console_device(struct console *c, int *index)
+{
+ *index = 0;
+ return g_dcc_tty_driver;
+}
+
+static int __init dcc_console_setup(struct console *co, char *options)
+{
+ if (co->index != 0)
+ return -ENODEV;
+ return 0;
+}
+
+
+static struct console dcc_console =
+{
+ .name = "ttyDCC",
+ .write = dcc_console_write,
+ .device = dcc_console_device,
+ .setup = dcc_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+};
+
+static struct tty_operations dcc_tty_ops = {
+ .open = dcc_tty_open,
+ .close = dcc_tty_close,
+ .write = dcc_tty_write,
+ .write_room = dcc_tty_write_room,
+ .chars_in_buffer = dcc_tty_chars_in_buffer,
+ .unthrottle = dcc_tty_unthrottle,
+};
+
+static int __init dcc_tty_init(void)
+{
+ int ret;
+
+ hrtimer_init(&g_dcc_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ g_dcc_timer.function = dcc_tty_timer_func;
+
+ g_dcc_tty_driver = alloc_tty_driver(1);
+ if (!g_dcc_tty_driver) {
+ printk(KERN_ERR "dcc_tty_probe: alloc_tty_driver failed\n");
+ ret = -ENOMEM;
+ goto err_alloc_tty_driver_failed;
+ }
+ g_dcc_tty_driver->owner = THIS_MODULE;
+ g_dcc_tty_driver->driver_name = "dcc";
+ g_dcc_tty_driver->name = "ttyDCC";
+ g_dcc_tty_driver->major = 0; // auto assign
+ g_dcc_tty_driver->minor_start = 0;
+ g_dcc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ g_dcc_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+ g_dcc_tty_driver->init_termios = tty_std_termios;
+ g_dcc_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+ tty_set_operations(g_dcc_tty_driver, &dcc_tty_ops);
+ ret = tty_register_driver(g_dcc_tty_driver);
+ if (ret) {
+ printk(KERN_ERR "dcc_tty_probe: tty_register_driver failed, %d\n", ret);
+ goto err_tty_register_driver_failed;
+ }
+ tty_register_device(g_dcc_tty_driver, 0, NULL);
+
+ register_console(&dcc_console);
+ hrtimer_start(&g_dcc_timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+
+ return 0;
+
+err_tty_register_driver_failed:
+ put_tty_driver(g_dcc_tty_driver);
+ g_dcc_tty_driver = NULL;
+err_alloc_tty_driver_failed:
+ return ret;
+}
+
+static void __exit dcc_tty_exit(void)
+{
+ int ret;
+
+ tty_unregister_device(g_dcc_tty_driver, 0);
+ ret = tty_unregister_driver(g_dcc_tty_driver);
+ if (ret < 0) {
+ printk(KERN_ERR "dcc_tty_remove: tty_unregister_driver failed, %d\n", ret);
+ } else {
+ put_tty_driver(g_dcc_tty_driver);
+ }
+ g_dcc_tty_driver = NULL;
+}
+
+module_init(dcc_tty_init);
+module_exit(dcc_tty_exit);
+
+
diff --git a/drivers/char/exynos_mem.c b/drivers/char/exynos_mem.c
new file mode 100644
index 0000000..85c7a29
--- /dev/null
+++ b/drivers/char/exynos_mem.c
@@ -0,0 +1,286 @@
+/* linux/drivers/char/exynos_mem.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * 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.
+*/
+
+#include <linux/errno.h> /* error codes */
+#include <linux/fs.h>
+#include <linux/ioctl.h>
+#include <linux/memblock.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/highmem.h>
+#include <linux/dma-mapping.h>
+#include <asm/cacheflush.h>
+
+#include <plat/cpu.h>
+
+#include <linux/exynos_mem.h>
+
+#define L2_FLUSH_ALL SZ_1M
+#define L1_FLUSH_ALL SZ_64K
+
+struct exynos_mem {
+ bool cacheable;
+ unsigned int phybase;
+};
+
+int exynos_mem_open(struct inode *inode, struct file *filp)
+{
+ struct exynos_mem *prv_data;
+
+ prv_data = kzalloc(sizeof(struct exynos_mem), GFP_KERNEL);
+ if (!prv_data) {
+ pr_err("%s: not enough memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ prv_data->cacheable = true; /* Default: cacheable */
+
+ filp->private_data = prv_data;
+
+ printk(KERN_DEBUG "[%s:%d] private_data(0x%08x)\n",
+ __func__, __LINE__, (u32)prv_data);
+
+ return 0;
+}
+
+int exynos_mem_release(struct inode *inode, struct file *filp)
+{
+ printk(KERN_DEBUG "[%s:%d] private_data(0x%08x)\n",
+ __func__, __LINE__, (u32)filp->private_data);
+
+ kfree(filp->private_data);
+
+ return 0;
+}
+
+enum cacheop { EM_CLEAN, EM_INV, EM_FLUSH };
+
+static void cache_maint_inner(void *vaddr, size_t size, enum cacheop op)
+{
+ switch (op) {
+ case EM_CLEAN:
+ dmac_map_area(vaddr, size, DMA_TO_DEVICE);
+ break;
+ case EM_INV:
+ dmac_unmap_area(vaddr, size, DMA_TO_DEVICE);
+ break;
+ case EM_FLUSH:
+ dmac_flush_range(vaddr, vaddr + size);
+ }
+}
+
+static void cache_maint_phys(phys_addr_t start, size_t length, enum cacheop op)
+{
+ size_t left = length;
+ phys_addr_t begin = start;
+
+ if (!soc_is_exynos5250() && !soc_is_exynos5210()) {
+ if (length > (size_t) L1_FLUSH_ALL) {
+ flush_cache_all();
+ smp_call_function(
+ (smp_call_func_t)__cpuc_flush_kern_all,
+ NULL, 1);
+
+ goto outer_cache_ops;
+ }
+ }
+
+#ifdef CONFIG_HIGHMEM
+ do {
+ size_t len;
+ struct page *page;
+ void *vaddr;
+ off_t offset;
+
+ page = phys_to_page(start);
+ offset = offset_in_page(start);
+ len = PAGE_SIZE - offset;
+
+ if (left < len)
+ len = left;
+
+ if (PageHighMem(page)) {
+ vaddr = kmap(page);
+ cache_maint_inner(vaddr + offset, len, op);
+ kunmap(page);
+ } else {
+ vaddr = page_address(page) + offset;
+ cache_maint_inner(vaddr, len, op);
+ }
+ left -= len;
+ start += len;
+ } while (left);
+#else
+ cache_maint_inner(phys_to_virt(begin), left, op);
+#endif
+
+outer_cache_ops:
+ switch (op) {
+ case EM_CLEAN:
+ outer_clean_range(begin, begin + length);
+ break;
+ case EM_INV:
+ if (length <= L2_FLUSH_ALL) {
+ outer_inv_range(begin, begin + length);
+ break;
+ }
+ /* else FALL THROUGH */
+ case EM_FLUSH:
+ outer_flush_range(begin, begin + length);
+ break;
+ }
+}
+
+static void exynos_mem_paddr_cache_clean(dma_addr_t start, size_t length)
+{
+ if (length > (size_t) L2_FLUSH_ALL) {
+ flush_cache_all(); /* L1 */
+ smp_call_function((smp_call_func_t)__cpuc_flush_kern_all, NULL, 1);
+ outer_clean_all(); /* L2 */
+ } else if (length > (size_t) L1_FLUSH_ALL) {
+ dma_addr_t end = start + length - 1;
+
+ flush_cache_all(); /* L1 */
+ smp_call_function((smp_call_func_t)__cpuc_flush_kern_all, NULL, 1);
+ outer_clean_range(start, end); /* L2 */
+ } else {
+ dma_addr_t end = start + length - 1;
+
+ dmac_flush_range(phys_to_virt(start), phys_to_virt(end));
+ outer_clean_range(start, end); /* L2 */
+ }
+}
+
+long exynos_mem_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case EXYNOS_MEM_SET_CACHEABLE:
+ {
+ struct exynos_mem *mem = filp->private_data;
+ int cacheable;
+ if (get_user(cacheable, (u32 __user *)arg)) {
+ pr_err("[%s:%d] err: EXYNOS_MEM_SET_CACHEABLE\n",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+ mem->cacheable = cacheable;
+ break;
+ }
+
+ case EXYNOS_MEM_PADDR_CACHE_FLUSH:
+ {
+ struct exynos_mem_flush_range range;
+ if (copy_from_user(&range,
+ (struct exynos_mem_flush_range __user *)arg,
+ sizeof(range))) {
+ pr_err("[%s:%d] err: EXYNOS_MEM_PADDR_CACHE_FLUSH\n",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ cache_maint_phys(range.start, range.length, EM_FLUSH);
+ break;
+ }
+ case EXYNOS_MEM_PADDR_CACHE_CLEAN:
+ {
+ struct exynos_mem_flush_range range;
+ if (copy_from_user(&range,
+ (struct exynos_mem_flush_range __user *)arg,
+ sizeof(range))) {
+ pr_err("[%s:%d] err: EXYNOS_MEM_PADDR_CACHE_FLUSH\n",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ cache_maint_phys(range.start, range.length, EM_CLEAN);
+ break;
+ }
+ case EXYNOS_MEM_SET_PHYADDR:
+ {
+ struct exynos_mem *mem = filp->private_data;
+ int phyaddr;
+ if (get_user(phyaddr, (u32 __user *)arg)) {
+ pr_err("[%s:%d] err: EXYNOS_MEM_SET_PHYADDR\n",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+ mem->phybase = phyaddr >> PAGE_SHIFT;
+
+ break;
+ }
+
+ default:
+ pr_err("[%s:%d] error command\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void exynos_mem_mmap_open(struct vm_area_struct *vma)
+{
+ printk(KERN_DEBUG "[%s] addr(0x%08x)\n", __func__, (u32)vma->vm_start);
+}
+
+static void exynos_mem_mmap_close(struct vm_area_struct *vma)
+{
+ printk(KERN_DEBUG "[%s] addr(0x%08x)\n", __func__, (u32)vma->vm_start);
+}
+
+static struct vm_operations_struct exynos_mem_ops = {
+ .open = exynos_mem_mmap_open,
+ .close = exynos_mem_mmap_close,
+};
+
+int exynos_mem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct exynos_mem *mem = (struct exynos_mem *)filp->private_data;
+ bool cacheable = mem->cacheable;
+ dma_addr_t start = 0;
+ u32 pfn = 0;
+ u32 size = vma->vm_end - vma->vm_start;
+
+ if (vma->vm_pgoff) {
+ start = vma->vm_pgoff << PAGE_SHIFT;
+ pfn = vma->vm_pgoff;
+ } else {
+ start = mem->phybase << PAGE_SHIFT;
+ pfn = mem->phybase;
+ }
+
+ /* TODO: currently lowmem is only avaiable */
+ if ((phys_to_virt(start) < (void *)PAGE_OFFSET) ||
+ (phys_to_virt(start) >= high_memory)) {
+ pr_err("[%s] invalid paddr(0x%08x)\n", __func__, start);
+ return -EINVAL;
+ }
+
+ if (!cacheable)
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ vma->vm_flags |= VM_RESERVED;
+ vma->vm_ops = &exynos_mem_ops;
+
+ if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED)) {
+ pr_err("writable mapping must be shared\n");
+ return -EINVAL;
+ }
+
+ if (remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot)) {
+ pr_err("mmap fail\n");
+ return -EINVAL;
+ }
+
+ vma->vm_ops->open(vma);
+
+ return 0;
+}
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 8fc04b4..3f4e40f 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -34,6 +34,16 @@
# include <linux/efi.h>
#endif
+#ifdef CONFIG_S3C_MEM
+# include "s3c_mem.h"
+#ifdef CONFIG_S3C_MEM_CMA_ALLOC
+#include <linux/cma.h>
+#include <linux/platform_device.h>
+#define S3CMEM_NAME "s3c-mem"
+
+#endif
+#endif
+
static inline unsigned long size_inside_page(unsigned long start,
unsigned long size)
{
@@ -56,6 +66,7 @@ static inline int valid_mmap_phys_addr_range(unsigned long pfn, size_t size)
}
#endif
+#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM)
#ifdef CONFIG_STRICT_DEVMEM
static inline int range_is_allowed(unsigned long pfn, unsigned long size)
{
@@ -81,7 +92,9 @@ static inline int range_is_allowed(unsigned long pfn, unsigned long size)
return 1;
}
#endif
+#endif
+#ifdef CONFIG_DEVMEM
void __weak unxlate_dev_mem_ptr(unsigned long phys, void *addr)
{
}
@@ -208,6 +221,9 @@ static ssize_t write_mem(struct file *file, const char __user *buf,
*ppos += written;
return written;
}
+#endif /* CONFIG_DEVMEM */
+
+#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM)
int __weak phys_mem_access_prot_allowed(struct file *file,
unsigned long pfn, unsigned long size, pgprot_t *vma_prot)
@@ -329,6 +345,7 @@ static int mmap_mem(struct file *file, struct vm_area_struct *vma)
}
return 0;
}
+#endif /* CONFIG_DEVMEM */
#ifdef CONFIG_DEVKMEM
static int mmap_kmem(struct file *file, struct vm_area_struct *vma)
@@ -693,6 +710,8 @@ static loff_t null_lseek(struct file *file, loff_t offset, int orig)
return file->f_pos = 0;
}
+#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) || defined(CONFIG_DEVPORT)
+
/*
* The memory devices use the full 32/64 bits of the offset, and so we cannot
* check against negative addresses: they are ok. The return value is weird,
@@ -726,10 +745,14 @@ static loff_t memory_lseek(struct file *file, loff_t offset, int orig)
return ret;
}
+#endif
+
+#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) || defined(CONFIG_DEVPORT)
static int open_port(struct inode * inode, struct file * filp)
{
return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
}
+#endif
#define zero_lseek null_lseek
#define full_lseek null_lseek
@@ -739,6 +762,7 @@ static int open_port(struct inode * inode, struct file * filp)
#define open_kmem open_mem
#define open_oldmem open_mem
+#ifdef CONFIG_DEVMEM
static const struct file_operations mem_fops = {
.llseek = memory_lseek,
.read = read_mem,
@@ -747,6 +771,7 @@ static const struct file_operations mem_fops = {
.open = open_mem,
.get_unmapped_area = get_unmapped_area_mem,
};
+#endif
#ifdef CONFIG_DEVKMEM
static const struct file_operations kmem_fops = {
@@ -806,6 +831,34 @@ static const struct file_operations oldmem_fops = {
};
#endif
+#ifdef CONFIG_S3C_MEM
+extern int s3c_mem_mmap(struct file* filp, struct vm_area_struct *vma);
+extern long s3c_mem_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+
+static const struct file_operations s3c_mem_fops = {
+#ifdef CONFIG_S3C_MEM_CMA_ALLOC
+ .open = s3c_mem_open,
+ .release = s3c_mem_release,
+#endif
+ .unlocked_ioctl = s3c_mem_ioctl,
+ .mmap = s3c_mem_mmap,
+};
+#endif
+
+#ifdef CONFIG_EXYNOS_MEM
+extern int exynos_mem_open(struct inode * inode, struct file *filp);
+extern int exynos_mem_release(struct inode * inode, struct file *filp);
+extern long exynos_mem_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+extern int exynos_mem_mmap(struct file* filp, struct vm_area_struct *vma);
+
+static const struct file_operations exynos_mem_fops = {
+ .open = exynos_mem_open,
+ .release = exynos_mem_release,
+ .unlocked_ioctl = exynos_mem_ioctl,
+ .mmap = exynos_mem_mmap,
+};
+#endif
+
static ssize_t kmsg_writev(struct kiocb *iocb, const struct iovec *iv,
unsigned long count, loff_t pos)
{
@@ -850,7 +903,9 @@ static const struct memdev {
const struct file_operations *fops;
struct backing_dev_info *dev_info;
} devlist[] = {
+#ifdef CONFIG_DEVMEM
[1] = { "mem", 0, &mem_fops, &directly_mappable_cdev_bdi },
+#endif
#ifdef CONFIG_DEVKMEM
[2] = { "kmem", 0, &kmem_fops, &directly_mappable_cdev_bdi },
#endif
@@ -866,6 +921,14 @@ static const struct memdev {
#ifdef CONFIG_CRASH_DUMP
[12] = { "oldmem", 0, &oldmem_fops, NULL },
#endif
+#ifdef CONFIG_S3C_MEM
+ [13] = {"s3c-mem", S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH
+ | S_IWOTH, &s3c_mem_fops},
+#endif
+#ifdef CONFIG_EXYNOS_MEM
+ [14] = {"exynos-mem", S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH
+ | S_IWOTH, &exynos_mem_fops},
+#endif
};
static int memory_open(struct inode *inode, struct file *filp)
@@ -913,7 +976,9 @@ static int __init chr_dev_init(void)
{
int minor;
int err;
-
+#ifdef CONFIG_S3C_MEM_CMA_ALLOC
+ struct device *dev;
+#endif
err = bdi_init(&zero_bdi);
if (err)
return err;
@@ -929,11 +994,61 @@ static int __init chr_dev_init(void)
for (minor = 1; minor < ARRAY_SIZE(devlist); minor++) {
if (!devlist[minor].name)
continue;
+#ifndef CONFIG_S3C_MEM_CMA_ALLOC
device_create(mem_class, NULL, MKDEV(MEM_MAJOR, minor),
NULL, devlist[minor].name);
+#else
+ dev = device_create(mem_class, NULL, MKDEV(MEM_MAJOR, minor),
+ NULL, devlist[minor].name);
+ if (strncmp(devlist[minor].name, S3CMEM_NAME, 7) == 0) {
+#ifdef CONFIG_VIDEO_SAMSUNG_USE_DMA_MEM
+
+ s3c_mem_cma_dev =
+ kzalloc(sizeof(struct device), GFP_KERNEL);
+ memcpy(s3c_mem_cma_dev, dev, sizeof(struct device));
+#else
+ struct cma_info s_cma_mem_info;
+ dma_addr_t base_addr = 0;
+ int err, i = 0;
+ err = cma_info(&s_cma_mem_info, dev, 0);
+ if (err) {
+ printk(KERN_INFO "%s: get cma info failed\n",
+ __func__);
+ } else {
+ base_addr =
+ (dma_addr_t) cma_alloc(dev, S3CMEM_NAME,
+ (size_t)
+ s_cma_mem_info.
+ total_size, 0);
+ }
+
+ printk(KERN_INFO "base_addr = 0x%x\n", base_addr);
+ s3c_cma_block_size = SLOT_SIZE * SZ_1K;
+ s3c_cma_max_block_num =
+ (s_cma_mem_info.total_size / s3c_cma_block_size);
+ s3c_slot_info =
+ kzalloc(sizeof(struct s3c_slot_info) *
+ s3c_cma_max_block_num, GFP_KERNEL);
+ for (i = 0; i < s3c_cma_max_block_num; i++) {
+ s3c_slot_info[i].s_start_addr =
+ base_addr + (i * s3c_cma_block_size);
+ s3c_slot_info[i].s_end_addr =
+ s3c_slot_info[i].s_start_addr +
+ s3c_cma_block_size;
+ s3c_slot_info[i].s_size = s3c_cma_block_size;
+ s3c_slot_info[i].s_mapped = false;
+ }
+#endif
+ }
+#endif
+
}
return tty_init();
}
+#ifdef CONFIG_S3C_MEM_CMA_ALLOC
+late_initcall(chr_dev_init);
+#else
fs_initcall(chr_dev_init);
+#endif
diff --git a/drivers/char/s3c_mem.c b/drivers/char/s3c_mem.c
new file mode 100644
index 0000000..7645dbe
--- /dev/null
+++ b/drivers/char/s3c_mem.c
@@ -0,0 +1,777 @@
+/* drivers/char/s3c_mem.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * S3C MEM driver for /dev/mem
+ *
+ * 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.
+*/
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/timer.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/errno.h> /* error codes */
+#include <asm/div64.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <asm/irq.h>
+#include <asm/cacheflush.h>
+#include <linux/slab.h>
+#include <linux/mman.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/unistd.h>
+#include <linux/version.h>
+#include <mach/map.h>
+#include <mach/hardware.h>
+
+#include "s3c_mem.h"
+#ifdef CONFIG_S3C_DMA_MEM
+#include "s3c_dma_mem.h"
+#endif
+
+#ifdef CONFIG_S3C_MEM_CMA_ALLOC
+#include <linux/cma.h>
+#include <linux/platform_device.h>
+#endif
+
+static int flag;
+
+static unsigned int physical_address;
+
+#ifdef USE_DMA_ALLOC
+static unsigned int virtual_address;
+#endif
+
+#ifdef CONFIG_S3C_MEM_CMA_ALLOC
+
+#ifdef CONFIG_VIDEO_SAMSUNG_USE_DMA_MEM
+struct device *s3c_mem_cma_dev;
+static struct device *s3c_mem_get_dev(void)
+{
+ return s3c_mem_cma_dev;
+}
+
+#else
+struct s3c_slot_info *s3c_slot_info;
+int s3c_cma_max_block_num;
+int s3c_cma_block_size;
+
+
+static void s3c_mem_log(struct s3c_dev_info *prv_data, bool mem_info)
+{
+ int i = 0;
+ for (i = 0; i < prv_data->dev_max_slot_num; i++)
+ printk(KERN_INFO
+ "s_slot_info[%d].s_start_addr=0x%x s_mapped=%d\n", i,
+ prv_data->s_slot_info[i].s_start_addr,
+ prv_data->s_slot_info[i].s_mapped);
+ if (mem_info)
+ printk(KERN_INFO
+ "s_cur_mem_info->paddr=0x%x s_mem_info->vaddr=0x%x s_mem_info->size=%d\n",
+ prv_data->s_cur_mem_info.paddr,
+ prv_data->s_cur_mem_info.vaddr,
+ prv_data->s_cur_mem_info.mapped_size);
+}
+
+static unsigned long s3c_mapping_slot(struct s3c_dev_info *prv_data)
+{
+ int i, j, k, v_start_slot = 0;
+ unsigned long lv_ret = 0;
+
+ for (i = 0; i < prv_data->dev_max_slot_num; i++) {
+ if (prv_data->s_slot_info[i].s_mapped == false) {
+ if (i + prv_data->s_cur_mem_info.req_memblock >
+ prv_data->dev_max_slot_num) {
+ printk(KERN_ERR "ERROR : not enough memory\n");
+ return lv_ret;
+ }
+ v_start_slot = i;
+ for (j = i;
+ j < i + prv_data->s_cur_mem_info.req_memblock;
+ j++) {
+ if (prv_data->s_slot_info[j].s_mapped == true)
+ break;
+ }
+ if (j == i + prv_data->s_cur_mem_info.req_memblock) {
+ lv_ret =
+ __phys_to_pfn(prv_data->s_slot_info
+ [v_start_slot].s_start_addr);
+ physical_address = (unsigned int)
+ prv_data->s_slot_info[v_start_slot].
+ s_start_addr;
+ for (k = v_start_slot; k < j; k++) {
+ prv_data->s_slot_info[k].s_mapped =
+ true;
+ printk(KERN_INFO
+ "prv_data->s_slot_info[%d].s_mapped=1\n",
+ k);
+ }
+ break;
+ }
+ } else
+ continue;
+ }
+ if (i == prv_data->dev_max_slot_num)
+ printk(KERN_ERR "ERROR :can not find the suitable slot\n");
+
+ return lv_ret;
+}
+
+static int s3c_unmapping_slot(struct s3c_dev_info *prv_data)
+{
+ int i, j, lv_ret = 0;
+ for (i = 0; i < prv_data->dev_max_slot_num; i++) {
+ if (prv_data->s_slot_info[i].s_start_addr ==
+ prv_data->s_cur_mem_info.paddr) {
+ for (j = i;
+ j < i + prv_data->s_cur_mem_info.req_memblock;
+ j++) {
+ prv_data->s_slot_info[j].s_mapped = false;
+ printk(KERN_INFO
+ "s_slot_info[%d].s_mapped = 0\n", j);
+ }
+ }
+ }
+ return lv_ret;
+}
+#endif
+
+int s3c_mem_open(struct inode *inode, struct file *filp)
+{
+ struct s3c_dev_info *prv_data;
+ mutex_lock(&mem_open_lock);
+
+ prv_data = kzalloc(sizeof(struct s3c_dev_info), GFP_KERNEL);
+ if (!prv_data) {
+ pr_err("%s: not enough memory\n", __func__);
+ return -ENOMEM;
+ }
+#ifdef CONFIG_VIDEO_SAMSUNG_USE_DMA_MEM
+ prv_data->s3c_mem_cma_dev = s3c_mem_get_dev();
+ prv_data->s_cur_mem_info.phy_addr = 0;
+ prv_data->s_cur_mem_info.vir_addr = 0;
+ prv_data->s_cur_mem_info.size = 0;
+#else
+ prv_data->s_slot_info = s3c_slot_info;
+ prv_data->dev_slot_size = s3c_cma_block_size;
+ prv_data->dev_max_slot_num = s3c_cma_max_block_num;
+ prv_data->s_cur_mem_info.paddr = 0;
+ prv_data->s_cur_mem_info.vaddr = 0;
+ prv_data->s_cur_mem_info.mapped_size = 0;
+ prv_data->s_cur_mem_info.req_memblock = 0;
+#endif
+ filp->private_data = prv_data;
+
+ mutex_unlock(&mem_open_lock);
+
+ return 0;
+}
+
+int s3c_mem_release(struct inode *inode, struct file *filp)
+{
+ struct mm_struct *mm = current->mm;
+ struct s3c_dev_info *prv_data =
+ (struct s3c_dev_info *)filp->private_data;
+
+ mutex_lock(&mem_release_lock);
+#ifdef CONFIG_VIDEO_SAMSUNG_USE_DMA_MEM
+ if (prv_data->s_cur_mem_info.vir_addr) {
+ if (do_munmap
+ (mm, prv_data->s_cur_mem_info.vir_addr,
+ prv_data->s_cur_mem_info.size) < 0) {
+ printk(KERN_ERR "do_munmap() failed !!\n");
+ mutex_unlock(&mem_free_lock);
+ return -EINVAL;
+ }
+ if (prv_data->s_cur_mem_info.phy_addr)
+ cma_free(prv_data->s_cur_mem_info.phy_addr);
+ prv_data->s_cur_mem_info.vir_addr = 0;
+ prv_data->s_cur_mem_info.size = 0;
+ prv_data->s_cur_mem_info.phy_addr = 0;
+ DEBUG("do_munmap() succeed !!\n");
+ }
+#else
+
+ printk(KERN_INFO
+ "prv_data->s_cur_mem_info->paddr=0x%x vaddr=0x%x size=%d\n",
+ prv_data->s_cur_mem_info.paddr, prv_data->s_cur_mem_info.vaddr,
+ prv_data->s_cur_mem_info.mapped_size);
+
+ if (prv_data->s_cur_mem_info.vaddr) {
+ s3c_unmapping_slot(prv_data);
+ if (do_munmap
+ (mm, prv_data->s_cur_mem_info.vaddr,
+ prv_data->s_cur_mem_info.mapped_size) < 0) {
+ printk(KERN_ERR "do_munmap() failed !!\n");
+ mutex_unlock(&mem_release_lock);
+ return -EINVAL;
+ }
+ }
+#endif
+ kfree(filp->private_data);
+ filp->private_data = NULL;
+ mutex_unlock(&mem_release_lock);
+
+ return 0;
+}
+#endif
+
+long s3c_mem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+#ifdef USE_DMA_ALLOC
+ unsigned long virt_addr;
+#else
+ unsigned long *virt_addr;
+#endif
+
+ struct mm_struct *mm = current->mm;
+ struct s3c_mem_alloc param;
+ struct vm_area_struct *vma;
+ unsigned long start, this_pfn;
+#ifdef CONFIG_S3C_DMA_MEM
+ struct s3c_mem_dma_param dma_param;
+#endif
+
+ switch (cmd) {
+ case S3C_MEM_ALLOC:
+ mutex_lock(&mem_alloc_lock);
+ if (copy_from_user(&param, (struct s3c_mem_alloc *)arg,
+ sizeof(struct s3c_mem_alloc))) {
+ mutex_unlock(&mem_alloc_lock);
+ return -EFAULT;
+ }
+ flag = MEM_ALLOC;
+ param.vir_addr = do_mmap(file, 0, param.size,
+ (PROT_READ|PROT_WRITE), MAP_SHARED, 0);
+ DEBUG("param.vir_addr = %08x, %d\n",
+ param.vir_addr, __LINE__);
+ if (param.vir_addr == -EINVAL) {
+ printk(KERN_INFO "S3C_MEM_ALLOC FAILED\n");
+ flag = 0;
+ mutex_unlock(&mem_alloc_lock);
+ return -EFAULT;
+ }
+ param.phy_addr = physical_address;
+#ifdef USE_DMA_ALLOC
+ param.kvir_addr = virtual_address;
+#endif
+
+ DEBUG("KERNEL MALLOC : param.phy_addr = 0x%X \t "
+ "size = %d \t param.vir_addr = 0x%X, %d\n",
+ param.phy_addr, param.size, param.vir_addr,
+ __LINE__);
+
+ if (copy_to_user((struct s3c_mem_alloc *)arg, &param,
+ sizeof(struct s3c_mem_alloc))) {
+ flag = 0;
+ mutex_unlock(&mem_alloc_lock);
+ return -EFAULT;
+ }
+ flag = 0;
+ mutex_unlock(&mem_alloc_lock);
+
+ break;
+#ifdef CONFIG_S3C_MEM_CMA_ALLOC
+ case S3C_MEM_CMA_ALLOC:
+ {
+#ifdef CONFIG_VIDEO_SAMSUNG_USE_DMA_MEM
+ struct cma_info mem_info;
+ int err;
+#endif
+ struct s3c_dev_info *prv_data =
+ (struct s3c_dev_info *)file->private_data;
+
+ mutex_lock(&mem_alloc_lock);
+ if (copy_from_user(&param, (struct s3c_mem_alloc *)arg,
+ sizeof(struct s3c_mem_alloc))) {
+ mutex_unlock(&mem_alloc_lock);
+ return -EFAULT;
+ }
+ flag = MEM_CMA_ALLOC;
+
+#ifdef CONFIG_VIDEO_SAMSUNG_USE_DMA_MEM
+ err = cma_info(&mem_info, prv_data->s3c_mem_cma_dev, 0);
+ printk(KERN_DEBUG "%s : [cma_info] start_addr : 0x%x, end_addr : 0x%x, "
+ "total_size : 0x%x, free_size : 0x%x req_size : 0x%x\n",
+ __func__, mem_info.lower_bound,
+ mem_info.upper_bound, mem_info.total_size,
+ mem_info.free_size, param.size);
+
+ if (err || (mem_info.free_size < param.size)) {
+ printk(KERN_ERR "%s: get cma info failed\n",
+ __func__);
+ mutex_unlock(&mem_alloc_lock);
+ return -ENOMEM;
+ }
+ param.phy_addr = (dma_addr_t) cma_alloc
+ (prv_data->s3c_mem_cma_dev, "dma",
+ (size_t) param.size, 0);
+
+ printk(KERN_INFO "param.phy_addr = 0x%x\n",
+ param.phy_addr);
+ if (!param.phy_addr) {
+ printk(KERN_ERR "%s: cma_alloc failed\n",
+ __func__);
+
+ mutex_unlock(&mem_alloc_lock);
+ return -ENOMEM;
+ }
+ prv_data->s_cur_mem_info.size = param.size;
+ prv_data->s_cur_mem_info.phy_addr = param.phy_addr;
+#endif
+
+ param.vir_addr =
+ do_mmap(file, 0, param.size,
+ (PROT_READ | PROT_WRITE), MAP_SHARED, 0);
+ DEBUG("param.vir_addr = %08x, %d\n", param.vir_addr,
+ __LINE__);
+
+ if (param.vir_addr == -EINVAL) {
+ printk(KERN_ERR "S3C_MEM_ALLOC FAILED\n");
+ flag = 0;
+ mutex_unlock(&mem_alloc_lock);
+ return -EFAULT;
+ }
+#ifdef CONFIG_VIDEO_SAMSUNG_USE_DMA_MEM
+ prv_data->s_cur_mem_info.vir_addr = param.vir_addr;
+#else
+ param.phy_addr = physical_address;
+ printk(KERN_INFO "physical_address=0x%x\n",
+ physical_address);
+ prv_data->s_cur_mem_info.paddr = param.phy_addr;
+ prv_data->s_cur_mem_info.vaddr = param.vir_addr;
+ prv_data->s_cur_mem_info.mapped_size =
+ PAGE_ALIGN(param.size);
+#endif
+ if (copy_to_user((struct s3c_mem_alloc *)arg, &param,
+ sizeof(struct s3c_mem_alloc))) {
+ flag = 0;
+ mutex_unlock(&mem_alloc_lock);
+ return -EFAULT;
+ }
+ flag = 0;
+
+ mutex_unlock(&mem_alloc_lock);
+ }
+ break;
+
+#endif
+ case S3C_MEM_CACHEABLE_ALLOC:
+ mutex_lock(&mem_cacheable_alloc_lock);
+ if (copy_from_user(&param, (struct s3c_mem_alloc *)arg,
+ sizeof(struct s3c_mem_alloc))) {
+ mutex_unlock(&mem_cacheable_alloc_lock);
+ return -EFAULT;
+ }
+ flag = MEM_ALLOC_CACHEABLE;
+ param.vir_addr = do_mmap(file, 0, param.size,
+ (PROT_READ|PROT_WRITE), MAP_SHARED, 0);
+ DEBUG("param.vir_addr = %08x, %d\n",
+ param.vir_addr, __LINE__);
+ if (param.vir_addr == -EINVAL) {
+ printk(KERN_INFO "S3C_MEM_ALLOC FAILED\n");
+ flag = 0;
+ mutex_unlock(&mem_cacheable_alloc_lock);
+ return -EFAULT;
+ }
+ param.phy_addr = physical_address;
+ DEBUG("KERNEL MALLOC : param.phy_addr = 0x%X"
+ " \t size = %d \t param.vir_addr = 0x%X, %d\n",
+ param.phy_addr, param.size, param.vir_addr,
+ __LINE__);
+
+ if (copy_to_user((struct s3c_mem_alloc *)arg, &param,
+ sizeof(struct s3c_mem_alloc))) {
+ flag = 0;
+ mutex_unlock(&mem_cacheable_alloc_lock);
+ return -EFAULT;
+ }
+ flag = 0;
+ mutex_unlock(&mem_cacheable_alloc_lock);
+
+ break;
+
+ case S3C_MEM_SHARE_ALLOC:
+ mutex_lock(&mem_share_alloc_lock);
+ if (copy_from_user(&param, (struct s3c_mem_alloc *)arg,
+ sizeof(struct s3c_mem_alloc))) {
+ mutex_unlock(&mem_share_alloc_lock);
+ return -EFAULT;
+ }
+ flag = MEM_ALLOC_SHARE;
+ physical_address = param.phy_addr;
+ DEBUG("param.phy_addr = %08x, %d\n",
+ physical_address, __LINE__);
+ param.vir_addr = do_mmap(file, 0, param.size,
+ (PROT_READ|PROT_WRITE), MAP_SHARED, 0);
+ DEBUG("param.vir_addr = %08x, %d\n",
+ param.vir_addr, __LINE__);
+ if (param.vir_addr == -EINVAL) {
+ printk(KERN_INFO "S3C_MEM_SHARE_ALLOC FAILED\n");
+ flag = 0;
+ mutex_unlock(&mem_share_alloc_lock);
+ return -EFAULT;
+ }
+ DEBUG("MALLOC_SHARE : param.phy_addr = 0x%X \t "
+ "size = %d \t param.vir_addr = 0x%X, %d\n",
+ param.phy_addr, param.size, param.vir_addr,
+ __LINE__);
+
+ if (copy_to_user((struct s3c_mem_alloc *)arg, &param,
+ sizeof(struct s3c_mem_alloc))) {
+ flag = 0;
+ mutex_unlock(&mem_share_alloc_lock);
+ return -EFAULT;
+ }
+ flag = 0;
+ mutex_unlock(&mem_share_alloc_lock);
+
+ break;
+
+ case S3C_MEM_CACHEABLE_SHARE_ALLOC:
+ mutex_lock(&mem_cacheable_share_alloc_lock);
+ if (copy_from_user(&param, (struct s3c_mem_alloc *)arg,
+ sizeof(struct s3c_mem_alloc))) {
+ mutex_unlock(&mem_cacheable_share_alloc_lock);
+ return -EFAULT;
+ }
+ flag = MEM_ALLOC_CACHEABLE_SHARE;
+ physical_address = param.phy_addr;
+ DEBUG("param.phy_addr = %08x, %d\n",
+ physical_address, __LINE__);
+ param.vir_addr = do_mmap(file, 0, param.size,
+ (PROT_READ|PROT_WRITE), MAP_SHARED, 0);
+ DEBUG("param.vir_addr = %08x, %d\n",
+ param.vir_addr, __LINE__);
+ if (param.vir_addr == -EINVAL) {
+ printk(KERN_INFO "S3C_MEM_SHARE_ALLOC FAILED\n");
+ flag = 0;
+ mutex_unlock(&mem_cacheable_share_alloc_lock);
+ return -EFAULT;
+ }
+ DEBUG("MALLOC_SHARE : param.phy_addr = 0x%X \t "
+ "size = %d \t param.vir_addr = 0x%X, %d\n",
+ param.phy_addr, param.size, param.vir_addr,
+ __LINE__);
+
+ if (copy_to_user((struct s3c_mem_alloc *)arg, &param,
+ sizeof(struct s3c_mem_alloc))) {
+ flag = 0;
+ mutex_unlock(&mem_cacheable_share_alloc_lock);
+ return -EFAULT;
+ }
+ flag = 0;
+ mutex_unlock(&mem_cacheable_share_alloc_lock);
+
+ break;
+
+ case S3C_MEM_FREE:
+ mutex_lock(&mem_free_lock);
+ if (copy_from_user(&param, (struct s3c_mem_alloc *)arg,
+ sizeof(struct s3c_mem_alloc))) {
+ mutex_unlock(&mem_free_lock);
+ return -EFAULT;
+ }
+
+ DEBUG("KERNEL FREE : param.phy_addr = 0x%X \t "
+ "size = %d \t param.vir_addr = 0x%X, %d\n",
+ param.phy_addr, param.size, param.vir_addr,
+ __LINE__);
+
+ if (do_munmap(mm, param.vir_addr, param.size) < 0) {
+ printk(KERN_INFO "do_munmap() failed !!\n");
+ mutex_unlock(&mem_free_lock);
+ return -EINVAL;
+ }
+
+#ifdef USE_DMA_ALLOC
+ virt_addr = param.kvir_addr;
+ dma_free_writecombine(NULL, param.size,
+ (unsigned int *) virt_addr, param.phy_addr);
+#else
+ virt_addr = (unsigned long *)phys_to_virt(param.phy_addr);
+ kfree(virt_addr);
+#endif
+ param.size = 0;
+ DEBUG("do_munmap() succeed !!\n");
+
+ if (copy_to_user((struct s3c_mem_alloc *)arg, &param,
+ sizeof(struct s3c_mem_alloc))) {
+ mutex_unlock(&mem_free_lock);
+ return -EFAULT;
+ }
+
+ mutex_unlock(&mem_free_lock);
+
+ break;
+#ifdef CONFIG_S3C_MEM_CMA_ALLOC
+ case S3C_MEM_CMA_FREE:
+ {
+ struct s3c_dev_info *prv_data =
+ (struct s3c_dev_info *)file->private_data;
+
+ mutex_lock(&mem_free_lock);
+ if (copy_from_user(&param, (struct s3c_mem_alloc *)arg,
+ sizeof(struct s3c_mem_alloc))) {
+ mutex_unlock(&mem_free_lock);
+ return -EFAULT;
+ }
+
+ DEBUG("KERNEL FREE : param.phy_addr = 0x%X \t "
+ "size = %d \t param.vir_addr = 0x%X, %d\n",
+ param.phy_addr, param.size, param.vir_addr,
+ __LINE__);
+
+ printk
+ ("FREE : pa = 0x%x size = %d va = 0x%x\n",
+ param.phy_addr, param.size, param.vir_addr);
+#ifdef CONFIG_VIDEO_SAMSUNG_USE_DMA_MEM
+ if (param.vir_addr) {
+ if (do_munmap(mm, param.vir_addr, param.size) <
+ 0) {
+ printk(KERN_ERR
+ "do_munmap() failed !!\n");
+ mutex_unlock(&mem_free_lock);
+ return -EINVAL;
+ }
+ if (prv_data->s_cur_mem_info.phy_addr) {
+ cma_free(prv_data->
+ s_cur_mem_info.phy_addr);
+ }
+
+ param.size = 0;
+ prv_data->s_cur_mem_info.vir_addr = 0;
+ prv_data->s_cur_mem_info.size = 0;
+ prv_data->s_cur_mem_info.phy_addr = 0;
+ DEBUG("do_munmap() succeed !!\n");
+ }
+#else
+ if (param.vir_addr) {
+ s3c_unmapping_slot(prv_data);
+
+ if (do_munmap(mm, param.vir_addr, param.size) <
+ 0) {
+ printk(KERN_ERR
+ "do_munmap() failed !!\n");
+ mutex_unlock(&mem_free_lock);
+ return -EINVAL;
+ }
+ param.size = 0;
+ prv_data->s_cur_mem_info.paddr = 0;
+ prv_data->s_cur_mem_info.vaddr = 0;
+ prv_data->s_cur_mem_info.mapped_size = 0;
+ prv_data->s_cur_mem_info.req_memblock = 0;
+ DEBUG("do_munmap() succeed !!\n");
+ }
+#endif
+ if (copy_to_user((struct s3c_mem_alloc *)arg, &param,
+ sizeof(struct s3c_mem_alloc))) {
+ mutex_unlock(&mem_free_lock);
+ return -EFAULT;
+ }
+
+ mutex_unlock(&mem_free_lock);
+ }
+ break;
+#endif
+ case S3C_MEM_SHARE_FREE:
+ mutex_lock(&mem_share_free_lock);
+ if (copy_from_user(&param, (struct s3c_mem_alloc *)arg,
+ sizeof(struct s3c_mem_alloc))) {
+ mutex_unlock(&mem_share_free_lock);
+ return -EFAULT; }
+
+ DEBUG("MEM_SHARE_FREE : param.phy_addr = 0x%X \t "
+ "size = %d \t param.vir_addr = 0x%X, %d\n",
+ param.phy_addr, param.size, param.vir_addr,
+ __LINE__);
+
+ if (do_munmap(mm, param.vir_addr, param.size) < 0) {
+ printk(KERN_INFO "do_munmap() failed - MEM_SHARE_FREE!!\n");
+ mutex_unlock(&mem_share_free_lock);
+ return -EINVAL;
+ }
+
+ param.vir_addr = 0;
+ DEBUG("do_munmap() succeed !! - MEM_SHARE_FREE\n");
+
+ if (copy_to_user((struct s3c_mem_alloc *)arg, &param,
+ sizeof(struct s3c_mem_alloc))) {
+ mutex_unlock(&mem_share_free_lock);
+ return -EFAULT;
+ }
+
+ mutex_unlock(&mem_share_free_lock);
+
+ break;
+
+#ifdef CONFIG_S3C_DMA_MEM
+ case S3C_MEM_DMA_COPY:
+ if (copy_from_user(&dma_param, (struct s3c_mem_dma_param *)arg,
+ sizeof(struct s3c_mem_dma_param))) {
+ return -EFAULT;
+ }
+ if (s3c_dma_mem_start(current->mm, &dma_param,
+ S3C_DMA_MEM2MEM)) {
+ return -EINVAL;
+ }
+ if (copy_to_user((struct s3c_mem_dma_param *)arg, &dma_param,
+ sizeof(struct s3c_mem_dma_param))) {
+ return -EFAULT;
+ }
+ break;
+#endif
+
+ case S3C_MEM_GET_PADDR:
+ if (copy_from_user(&param, (struct s3c_mem_alloc *)arg,
+ sizeof(struct s3c_mem_alloc))) {
+ return -EFAULT;
+ }
+ start = param.vir_addr;
+ down_read(&mm->mmap_sem);
+ vma = find_vma(mm, start);
+
+ if (vma == NULL) {
+ up_read(&mm->mmap_sem);
+ return -EINVAL;
+ }
+
+ if (follow_pfn(vma, start, &this_pfn)) {
+ up_read(&mm->mmap_sem);
+ return -EINVAL;
+ }
+
+ param.phy_addr = this_pfn << PAGE_SHIFT;
+ up_read(&mm->mmap_sem);
+
+ if (copy_to_user((struct s3c_mem_alloc *)arg, &param,
+ sizeof(struct s3c_mem_alloc))) {
+ return -EFAULT;
+ }
+ break;
+
+ default:
+ DEBUG("s3c_mem_ioctl() : default !!\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(s3c_mem_ioctl);
+
+int s3c_mem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ unsigned long pageFrameNo = 0, size, phys_addr;
+
+#ifdef USE_DMA_ALLOC
+ unsigned long virt_addr;
+#else
+ unsigned long *virt_addr;
+#endif
+
+ size = vma->vm_end - vma->vm_start;
+
+ switch (flag) {
+ case MEM_ALLOC:
+ case MEM_ALLOC_CACHEABLE:
+
+#ifdef USE_DMA_ALLOC
+ virt_addr = (unsigned long)dma_alloc_writecombine(NULL, size,
+ (unsigned int *) &phys_addr,
+ GFP_KERNEL);
+#else
+ virt_addr = kmalloc(size, GFP_DMA | GFP_ATOMIC);
+#endif
+ if (!virt_addr) {
+ printk(KERN_INFO "kmalloc() failed !\n");
+ return -EINVAL;
+ }
+ DEBUG("MMAP_KMALLOC : virt addr = 0x%08x, size = %d, %d\n",
+ virt_addr, size, __LINE__);
+
+#ifndef USE_DMA_ALLOC
+ dmac_map_area(virt_addr, size / sizeof(unsigned long), 2);
+ phys_addr = virt_to_phys((unsigned long *)virt_addr);
+#endif
+ physical_address = (unsigned int)phys_addr;
+
+#ifdef USE_DMA_ALLOC
+ virtual_address = virt_addr;
+#endif
+ pageFrameNo = __phys_to_pfn(phys_addr);
+ break;
+#ifdef CONFIG_S3C_MEM_CMA_ALLOC
+ case MEM_CMA_ALLOC:
+ {
+ struct s3c_dev_info *prv_data =
+ (struct s3c_dev_info *)filp->private_data;
+ vma->vm_page_prot =
+ pgprot_writecombine(vma->vm_page_prot);
+#ifdef CONFIG_VIDEO_SAMSUNG_USE_DMA_MEM
+ pageFrameNo =
+ __phys_to_pfn(prv_data->s_cur_mem_info.phy_addr);
+#else
+ prv_data->s_cur_mem_info.req_memblock =
+ PAGE_ALIGN(size) / prv_data->dev_slot_size;
+
+ if (PAGE_ALIGN(size) % prv_data->dev_slot_size)
+ prv_data->s_cur_mem_info.req_memblock++;
+
+ printk(KERN_INFO "required slot=%d size=%lu\n",
+ prv_data->s_cur_mem_info.req_memblock, size);
+
+
+ pageFrameNo = s3c_mapping_slot(prv_data);
+#endif
+ if (!pageFrameNo) {
+ printk(KERN_ERR "mapping failed !\n");
+ return -EINVAL;
+ }
+
+ }
+ break;
+#endif
+ case MEM_ALLOC_SHARE:
+ case MEM_ALLOC_CACHEABLE_SHARE:
+ DEBUG("MMAP_KMALLOC_SHARE : phys addr = 0x%08x, %d\n",
+ physical_address, __LINE__);
+
+/* page frame number of the address for the physical_address to be shared. */
+ pageFrameNo = __phys_to_pfn(physical_address);
+ DEBUG("MMAP_KMALLOC_SHARE : vma->end = 0x%08x, "
+ "vma->start = 0x%08x, size = %d, %d\n",
+ vma->vm_end, vma->vm_start, size, __LINE__);
+ break;
+
+ default:
+ break;
+ }
+
+ if ((flag == MEM_ALLOC) || (flag == MEM_ALLOC_SHARE))
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ vma->vm_flags |= VM_RESERVED;
+
+ if (remap_pfn_range(vma, vma->vm_start, pageFrameNo,
+ size, vma->vm_page_prot)) {
+ printk(KERN_INFO "s3c_mem_mmap() : remap_pfn_range() failed !\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(s3c_mem_mmap);
diff --git a/drivers/char/s3c_mem.h b/drivers/char/s3c_mem.h
new file mode 100644
index 0000000..63971a7
--- /dev/null
+++ b/drivers/char/s3c_mem.h
@@ -0,0 +1,140 @@
+/* drivers/char/s3c_mem.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Header file for s3c mem
+ *
+ * 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 DEBUG_S3C_MEM
+#undef DEBUG_S3C_MEM
+
+#ifdef DEBUG_S3C_MEM
+#define DEBUG(fmt, args...) printk(fmt, ##args)
+#else
+#define DEBUG(fmt, args...) do {} while (0)
+#endif
+
+#ifdef CONFIG_S3C_DMA_MEM
+#include "s3c_dma_mem.h"
+#endif
+
+#define MEM_IOCTL_MAGIC 'M'
+
+#define S3C_MEM_ALLOC _IOWR(MEM_IOCTL_MAGIC, 310, struct s3c_mem_alloc)
+#define S3C_MEM_FREE _IOWR(MEM_IOCTL_MAGIC, 311, struct s3c_mem_alloc)
+
+#define S3C_MEM_SHARE_ALLOC _IOWR(MEM_IOCTL_MAGIC, 314, struct s3c_mem_alloc)
+#define S3C_MEM_SHARE_FREE _IOWR(MEM_IOCTL_MAGIC, 315, struct s3c_mem_alloc)
+
+#define S3C_MEM_CACHEABLE_ALLOC _IOWR(MEM_IOCTL_MAGIC, 316, struct s3c_mem_alloc)
+#define S3C_MEM_CACHEABLE_SHARE_ALLOC _IOWR(MEM_IOCTL_MAGIC, 317, struct s3c_mem_alloc)
+
+#ifdef CONFIG_S3C_DMA_MEM
+#define S3C_MEM_DMA_COPY _IOWR(MEM_IOCTL_MAGIC, 318, struct s3c_mem_dma_param)
+#endif
+
+#define S3C_MEM_GET_PADDR _IOWR(MEM_IOCTL_MAGIC, 320, struct s3c_mem_alloc)
+
+#ifdef CONFIG_S3C_MEM_CMA_ALLOC
+#define S3C_MEM_CMA_ALLOC \
+ _IOWR(MEM_IOCTL_MAGIC, 321, struct s3c_mem_alloc)
+#define S3C_MEM_CMA_FREE \
+ _IOWR(MEM_IOCTL_MAGIC, 322, struct s3c_mem_alloc)
+#endif
+
+#define MEM_ALLOC 1
+#define MEM_ALLOC_SHARE 2
+#define MEM_ALLOC_CACHEABLE 3
+#define MEM_ALLOC_CACHEABLE_SHARE 4
+
+#ifdef CONFIG_S3C_MEM_CMA_ALLOC
+#define MEM_CMA_ALLOC 5
+#endif
+
+#define S3C_MEM_MINOR 13
+#undef USE_DMA_ALLOC
+
+static DEFINE_MUTEX(mem_alloc_lock);
+static DEFINE_MUTEX(mem_free_lock);
+
+static DEFINE_MUTEX(mem_share_alloc_lock);
+static DEFINE_MUTEX(mem_share_free_lock);
+
+static DEFINE_MUTEX(mem_cacheable_alloc_lock);
+static DEFINE_MUTEX(mem_cacheable_share_alloc_lock);
+
+#ifdef CONFIG_S3C_MEM_CMA_ALLOC
+static DEFINE_MUTEX(mem_open_lock);
+static DEFINE_MUTEX(mem_release_lock);
+#endif
+
+struct s3c_mem_alloc {
+ int size;
+ unsigned int vir_addr;
+ unsigned int phy_addr;
+
+#ifdef USE_DMA_ALLOC
+ unsigned int kvir_addr;
+#endif
+};
+
+#ifdef CONFIG_S3C_DMA_MEM
+#define s3c_dma_init() s3c_dma_mem_init()
+#else
+#define s3c_dma_init() do { } while (0)
+#endif
+
+#ifdef CONFIG_S3C_MEM_CMA_ALLOC
+#ifdef CONFIG_VIDEO_SAMSUNG_SLOTSIZE_S3C_MEM_CMA
+#define SLOT_SIZE CONFIG_VIDEO_SAMSUNG_SLOTSIZE_S3C_MEM_CMA
+#else
+#define SLOT_SIZE 1024
+#endif
+
+extern int s3c_mem_open(struct inode *inode, struct file *filp);
+extern int s3c_mem_release(struct inode *inode, struct file *filp);
+
+#ifdef CONFIG_VIDEO_SAMSUNG_USE_DMA_MEM
+extern struct device *s3c_mem_cma_dev;
+
+struct s3c_dev_info {
+ struct s3c_mem_alloc s_cur_mem_info;
+ struct device *s3c_mem_cma_dev;
+};
+
+#else
+
+struct s3c_cur_mem_info {
+ unsigned int vaddr;
+ unsigned int paddr;
+ int mapped_size;
+ int req_memblock;
+};
+struct s3c_slot_info {
+ unsigned int s_start_addr;
+ unsigned int s_end_addr;
+ int s_size;
+ bool s_mapped;
+};
+
+struct s3c_dev_info {
+ struct s3c_cur_mem_info s_cur_mem_info;
+ struct s3c_slot_info *s_slot_info;
+ int dev_max_slot_num;
+ int dev_slot_size;
+
+};
+
+extern struct s3c_slot_info *s3c_slot_info;
+extern int s3c_cma_max_block_num;
+extern int s3c_cma_block_size;
+#endif
+
+
+
+#endif