| /* |
| * drivers/amlogic/secmon/secmon.c |
| * |
| * Copyright (C) 2017 Amlogic, Inc. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * 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/errno.h> |
| #include <linux/err.h> |
| #include <linux/of.h> |
| #include <linux/module.h> |
| #include <linux/of_fdt.h> |
| #include <linux/libfdt_env.h> |
| #include <linux/of_reserved_mem.h> |
| #include <linux/io.h> |
| #include <linux/platform_device.h> |
| #include <linux/dma-contiguous.h> |
| #include <asm/compiler.h> |
| #include <linux/cma.h> |
| #include <linux/arm-smccc.h> |
| #undef pr_fmt |
| #define pr_fmt(fmt) "secmon: " fmt |
| |
| static void __iomem *sharemem_in_base; |
| static void __iomem *sharemem_out_base; |
| static long phy_in_base; |
| static long phy_out_base; |
| #ifdef CONFIG_ARM64 |
| #define IN_SIZE 0x1000 |
| #else |
| #define IN_SIZE 0x1000 |
| #endif |
| #define OUT_SIZE 0x1000 |
| static DEFINE_MUTEX(sharemem_mutex); |
| #define DEV_REGISTED 1 |
| #define DEV_UNREGISTED 0 |
| static int secmon_dev_registed = DEV_UNREGISTED; |
| static long get_sharemem_info(unsigned int function_id) |
| { |
| struct arm_smccc_res res; |
| |
| arm_smccc_smc(function_id, 0, 0, 0, 0, 0, 0, 0, &res); |
| |
| return res.a0; |
| } |
| |
| #define RESERVE_MEM_SIZE 0x300000 |
| static int secmon_probe(struct platform_device *pdev) |
| { |
| struct device_node *np = pdev->dev.of_node; |
| unsigned int id; |
| int ret; |
| int mem_size; |
| struct page *page; |
| unsigned int clear[2] = {}; |
| |
| if (!of_property_read_u32(np, "in_base_func", &id)) |
| phy_in_base = get_sharemem_info(id); |
| |
| if (!of_property_read_u32(np, "out_base_func", &id)) |
| phy_out_base = get_sharemem_info(id); |
| |
| if (of_property_read_u32(np, "reserve_mem_size", &mem_size)) { |
| pr_err("can't get reserve_mem_size, use default value\n"); |
| mem_size = RESERVE_MEM_SIZE; |
| } else |
| pr_info("reserve_mem_size:0x%x\n", mem_size); |
| |
| ret = of_reserved_mem_device_init(&pdev->dev); |
| if (ret) { |
| pr_info("reserve memory init fail:%d\n", ret); |
| return ret; |
| } |
| secmon_dev_registed = DEV_REGISTED; |
| |
| if (of_property_read_u32_array(np, "clear_range", clear, 2)) |
| pr_info("can't fine clear_range\n"); |
| else |
| pr_info("clear_range:%x %x\n", clear[0], clear[1]); |
| |
| if (of_property_read_u32_array(np, "clear_range", clear, 2)) |
| pr_info("can't fine clear_range\n"); |
| else |
| pr_info("clear_range:%x %x\n", clear[0], clear[1]); |
| |
| page = dma_alloc_from_contiguous(&pdev->dev, mem_size >> PAGE_SHIFT, 0); |
| if (!page) { |
| pr_err("alloc page failed, ret:%p\n", page); |
| return -ENOMEM; |
| } |
| pr_info("get page:%p, %lx\n", page, page_to_pfn(page)); |
| |
| if (pfn_valid(__phys_to_pfn(phy_in_base))) |
| sharemem_in_base = (void __iomem *)__phys_to_virt(phy_in_base); |
| else |
| sharemem_in_base = ioremap_cache(phy_in_base, IN_SIZE); |
| |
| if (!sharemem_in_base) { |
| pr_info("secmon share mem in buffer remap fail!\n"); |
| return -ENOMEM; |
| } |
| |
| if (clear[0]) { |
| struct page *page = phys_to_page(clear[0]); |
| int cnt = clear[1] / PAGE_SIZE; |
| |
| cma_mmu_op(page, cnt, 0); |
| } |
| |
| if (pfn_valid(__phys_to_pfn(phy_out_base))) |
| sharemem_out_base = (void __iomem *) |
| __phys_to_virt(phy_out_base); |
| else |
| sharemem_out_base = ioremap_cache(phy_out_base, OUT_SIZE); |
| |
| if (!sharemem_out_base) { |
| pr_info("secmon share mem out buffer remap fail!\n"); |
| return -ENOMEM; |
| } |
| secmon_dev_registed = DEV_REGISTED; |
| pr_info("share in base: 0x%lx, share out base: 0x%lx\n", |
| (long)sharemem_in_base, (long)sharemem_out_base); |
| pr_info("phy_in_base: 0x%lx, phy_out_base: 0x%lx\n", |
| phy_in_base, phy_out_base); |
| |
| return ret; |
| } |
| |
| static const struct of_device_id secmon_dt_match[] = { |
| { .compatible = "amlogic, secmon" }, |
| { /* sentinel */ }, |
| }; |
| |
| static struct platform_driver secmon_platform_driver = { |
| .probe = secmon_probe, |
| .driver = { |
| .owner = THIS_MODULE, |
| .name = "secmon", |
| .of_match_table = secmon_dt_match, |
| }, |
| }; |
| |
| int __init meson_secmon_init(void) |
| { |
| int ret; |
| |
| ret = platform_driver_register(&secmon_platform_driver); |
| WARN((secmon_dev_registed != DEV_REGISTED), |
| "ERROR: secmon device must be enable!!!\n"); |
| return ret; |
| } |
| subsys_initcall(meson_secmon_init); |
| |
| void sharemem_mutex_lock(void) |
| { |
| mutex_lock(&sharemem_mutex); |
| } |
| void sharemem_mutex_unlock(void) |
| { |
| mutex_unlock(&sharemem_mutex); |
| } |
| |
| void __iomem *get_secmon_sharemem_input_base(void) |
| { |
| return sharemem_in_base; |
| } |
| void __iomem *get_secmon_sharemem_output_base(void) |
| { |
| return sharemem_out_base; |
| } |
| |
| long get_secmon_phy_input_base(void) |
| { |
| return phy_in_base; |
| } |
| long get_secmon_phy_output_base(void) |
| { |
| return phy_out_base; |
| } |