From c6da2cfeb05178a11c6d062a06f8078150ee492f Mon Sep 17 00:00:00 2001 From: codeworkx Date: Sat, 2 Jun 2012 13:09:29 +0200 Subject: samsung update 1 --- drivers/char/Kconfig | 47 +++ drivers/char/Makefile | 4 + drivers/char/dcc_tty.c | 326 +++++++++++++++++++ drivers/char/exynos_mem.c | 286 +++++++++++++++++ drivers/char/mem.c | 117 ++++++- drivers/char/s3c_mem.c | 777 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/char/s3c_mem.h | 140 +++++++++ 7 files changed, 1696 insertions(+), 1 deletion(-) create mode 100644 drivers/char/dcc_tty.c create mode 100644 drivers/char/exynos_mem.c create mode 100644 drivers/char/s3c_mem.c create mode 100644 drivers/char/s3c_mem.h (limited to 'drivers/char') 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 +#include +#include +#include +#include +#include +#include +#include + +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 /* error codes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#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 #endif +#ifdef CONFIG_S3C_MEM +# include "s3c_mem.h" +#ifdef CONFIG_S3C_MEM_CMA_ALLOC +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* error codes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "s3c_mem.h" +#ifdef CONFIG_S3C_DMA_MEM +#include "s3c_dma_mem.h" +#endif + +#ifdef CONFIG_S3C_MEM_CMA_ALLOC +#include +#include +#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(¶m, (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, ¶m, + 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(¶m, (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, ¶m, + 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(¶m, (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, ¶m, + 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(¶m, (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, ¶m, + 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(¶m, (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, ¶m, + 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(¶m, (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, ¶m, + 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(¶m, (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, ¶m, + 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(¶m, (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, ¶m, + 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(¶m, (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, ¶m, + 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 -- cgit v1.1