[9610] soc: samsung: epx: support dynamic loading framework
authorSangkyu Kim <skwith.kim@samsung.com>
Tue, 27 Mar 2018 08:26:37 +0000 (17:26 +0900)
committerhskang <hs1218.kang@samsung.com>
Tue, 28 Aug 2018 10:31:00 +0000 (19:31 +0900)
Change-Id: Ibc7ed92b8b668f0ade0460cf73c70397a9deff95
Signed-off-by: Sangkyu Kim <skwith.kim@samsung.com>
drivers/soc/samsung/Kconfig
drivers/soc/samsung/Makefile
drivers/soc/samsung/epx.c [new file with mode: 0644]

index b6c11535bfa8c8c06cd4c9f85276e4e652840552..515abffb8dc60aae27a47c5c2c77b3531ca13fc7 100644 (file)
@@ -30,6 +30,11 @@ config EXYNOS_BCM_DBG_DUMP
        help
          Enable exynos-bcm_dbg dump support
 
+config EPX
+       bool "Enable Exynos Performance Box"
+       default y
+       depends on ARCH_EXYNOS
+
 config EXYNOS_BCM
        bool "EXYNOS_BCM driver support"
        help
index 9d97ff810aae21e8eabfb539646e274bc2acf5ff..9ad490c156681f53af8b42300eb8a6fd84894d91 100644 (file)
@@ -7,6 +7,7 @@ obj-$(CONFIG_CAL_IF)            += cal-if/
 obj-$(CONFIG_ECT)              += ect_parser.o
 
 obj-$(CONFIG_EXYNOS_PMU)       += exynos-pmu.o
+obj-$(CONFIG_EPX)             += epx.o
 
 #FSYS0 TCXO
 obj-$(CONFIG_ARCH_EXYNOS)      += exynos-fsys0-tcxo.o
diff --git a/drivers/soc/samsung/epx.c b/drivers/soc/samsung/epx.c
new file mode 100644 (file)
index 0000000..5343035
--- /dev/null
@@ -0,0 +1,212 @@
+#include <asm/map.h>
+#include <asm/memory.h>
+#include <asm/uaccess.h>
+
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/of_address.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/memblock.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <asm/tlbflush.h>
+#include <asm/cacheflush.h>
+#include <linux/kallsyms.h>
+
+#define S5P_VA_EPX     (VMALLOC_START + 0xF6000000 + 0x03000000)
+#define EPX_SIZE       (1024 * 1024)
+
+static struct vm_struct epx_early_vm;
+static bool epx_loaded = false;
+
+typedef unsigned long (*pfn_kallsyms_lookup_name)(const char *name);
+typedef int (*pfn_binary_entry)(unsigned long address, pfn_kallsyms_lookup_name ksyms);
+
+struct page_change_data {
+       pgprot_t set_mask;
+       pgprot_t clear_mask;
+};
+
+static int __init epx_rmem_remap(void)
+{
+       unsigned long i;
+       pgprot_t prot = PAGE_KERNEL_EXEC;
+       int page_size, ret;
+       struct page *page;
+       struct page **pages;
+
+       page_size = epx_early_vm.size / PAGE_SIZE;
+       pages = kzalloc(sizeof(struct page*) * page_size, GFP_KERNEL);
+       page = phys_to_page(epx_early_vm.phys_addr);
+
+       for (i = 0; i < page_size; i++)
+               pages[i] = page++;
+
+       ret = map_vm_area(&epx_early_vm, prot, pages);
+       if (ret) {
+               pr_err("EPX: failed to map virtual memory area!\n");
+               kfree(pages);
+               return -ENOMEM;
+       }
+       kfree(pages);
+
+       epx_loaded = false;
+
+       return 0;
+}
+postcore_initcall(epx_rmem_remap);
+
+static int __init epx_mem_reserved_mem_setup(char *cmd)
+{
+       epx_early_vm.phys_addr = memblock_alloc(EPX_SIZE, SZ_4K);
+       epx_early_vm.size = EPX_SIZE + SZ_4K;
+       epx_early_vm.addr = (void *)S5P_VA_EPX;
+
+       vm_area_add_early(&epx_early_vm);
+
+       return 0;
+}
+__setup("epx_activate=", epx_mem_reserved_mem_setup);
+
+static int epx_change_page_range(pte_t *ptep, pgtable_t token, unsigned long addr,
+               void *data)
+{
+       struct page_change_data *cdata = data;
+       pte_t pte = *ptep;
+
+       pte = clear_pte_bit(pte, cdata->clear_mask);
+       pte = set_pte_bit(pte, cdata->set_mask);
+
+       set_pte(ptep, pte);
+       return 0;
+}
+
+static int epx_change_memory_common(unsigned long addr, int numpages,
+               pgprot_t set_mask, pgprot_t clear_mask)
+{
+       unsigned long start = addr;
+       unsigned long size = PAGE_SIZE*numpages;
+       unsigned long end = start + size;
+       int ret;
+       struct page_change_data data;
+
+       if (!PAGE_ALIGNED(addr)) {
+               start &= PAGE_MASK;
+               end = start + size;
+               WARN_ON_ONCE(1);
+       }
+
+       if (!numpages)
+               return 0;
+
+       data.set_mask = set_mask;
+       data.clear_mask = clear_mask;
+
+       ret = apply_to_page_range(&init_mm, start, size, epx_change_page_range,
+                       &data);
+
+       flush_tlb_kernel_range(start, end);
+       return ret;
+}
+
+static int epx_set_memory_rw(unsigned long addr, int numpages)
+{
+       return epx_change_memory_common(addr, numpages,
+                       __pgprot(PTE_WRITE),
+                       __pgprot(PTE_RDONLY));
+}
+
+static int epx_set_memory_nx(unsigned long addr, int numpages)
+{
+       return epx_change_memory_common(addr, numpages,
+                       __pgprot(PTE_PXN),
+                       __pgprot(0));
+}
+
+static int epx_set_memory_x(unsigned long addr, int numpages)
+{
+       return epx_change_memory_common(addr, numpages,
+                       __pgprot(0),
+                       __pgprot(PTE_PXN));
+}
+
+static ssize_t show_epx_activate(struct class *class,
+               struct class_attribute *attr, char *buf)
+{
+       long epx_fd;
+       struct file *fp;
+       mm_segment_t old_fs;
+       loff_t pos = 0;
+       char *binary_buffer;
+       pfn_binary_entry binary_entry;
+
+       if (epx_loaded)
+               return 0;
+
+       old_fs = get_fs();
+       set_fs(KERNEL_DS);
+
+       epx_fd = do_sys_open(AT_FDCWD, "/vendor/firmware/epx.bin", O_RDONLY | O_NOFOLLOW, 0664);
+       if (epx_fd < 0) {
+               pr_err("EPX : error to open file\n");
+               set_fs(old_fs);
+               return -EINVAL;
+       }
+
+       epx_set_memory_nx((unsigned long)epx_early_vm.addr, PFN_UP(EPX_SIZE));
+       epx_set_memory_rw((unsigned long)epx_early_vm.addr, PFN_UP(EPX_SIZE));
+
+       fp = fget(epx_fd);
+       if (fp) {
+               binary_buffer = kzalloc(EPX_SIZE, GFP_KERNEL);
+               if (binary_buffer != NULL) {
+
+                       vfs_read(fp, binary_buffer, EPX_SIZE, &pos);
+
+                       memcpy((void *)epx_early_vm.addr, binary_buffer, EPX_SIZE);
+                       fput(fp);
+
+                       flush_icache_range((unsigned long)(epx_early_vm.addr), (unsigned long)(epx_early_vm.addr + EPX_SIZE));
+                       epx_set_memory_x((unsigned long)epx_early_vm.addr, PFN_UP(EPX_SIZE));
+
+                       binary_entry = (pfn_binary_entry)epx_early_vm.addr;
+                       binary_entry((unsigned long)epx_early_vm.addr, kallsyms_lookup_name);
+
+                       kfree(binary_buffer);
+                       epx_loaded = true;
+               }
+
+       } else {
+               pr_err("[EPX] : error to convert file\n");
+       }
+
+       set_fs(old_fs);
+
+       return 0;
+}
+
+static struct class_attribute class_attr_epx_activate = __ATTR(epx_activate, S_IRUSR, show_epx_activate, NULL);
+
+static int __init epx_load(void)
+{
+       static struct class *epx_class;
+
+       epx_class = class_create(THIS_MODULE, "epx");
+       if (IS_ERR(epx_class)) {
+               pr_err("EPX : couldn't create class (%s : %d)\n", __FILE__, __LINE__);
+               return PTR_ERR(epx_class);
+       }
+
+       if (class_create_file(epx_class, &class_attr_epx_activate)) {
+               pr_err("EPX : couldn't create class (%s : %d)\n", __FILE__, __LINE__);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+late_initcall(epx_load);