| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * Copyright (c) 2019 Amlogic, Inc. All rights reserved. |
| */ |
| |
| #include <linux/cdev.h> |
| #include <linux/types.h> |
| #include <linux/device.h> |
| #include <linux/platform_device.h> |
| #include <linux/arm-smccc.h> |
| #include <linux/highmem.h> |
| #include <linux/slab.h> |
| #include <linux/io.h> |
| #include <linux/amlogic/efuse.h> |
| #include <linux/kallsyms.h> |
| #include "efuse.h" |
| |
| static DEFINE_MUTEX(efuse_lock); |
| |
| static unsigned long get_sharemem_info(unsigned long function_id) |
| { |
| struct arm_smccc_res res; |
| |
| arm_smccc_smc((unsigned long)function_id, 0, 0, 0, 0, 0, 0, 0, &res); |
| |
| return res.a0; |
| } |
| |
| static ssize_t meson_efuse_fn_smc(struct efuse_hal_api_arg *arg) |
| { |
| long ret; |
| unsigned int cmd, offset, size; |
| unsigned long *retcnt = (unsigned long *)(arg->retcnt); |
| struct arm_smccc_res res; |
| void *sharemem_in_base = NULL; |
| void *sharemem_out_base; |
| long phy_in_base = 0; |
| long phy_out_base = 0; |
| |
| if (arg->cmd == EFUSE_HAL_API_READ) |
| cmd = efuse_cmd.read_cmd; |
| else if (arg->cmd == EFUSE_HAL_API_WRITE) |
| cmd = efuse_cmd.write_cmd; |
| else |
| return -1; |
| |
| offset = arg->offset; |
| size = arg->size; |
| |
| mutex_lock(&efuse_lock); |
| |
| if (arg->cmd == EFUSE_HAL_API_WRITE) { |
| phy_in_base = get_sharemem_info(efuse_cmd.mem_in_base_cmd); |
| |
| if (!pfn_valid(__phys_to_pfn(phy_in_base))) |
| sharemem_in_base = ioremap_nocache(phy_in_base, size); |
| else |
| sharemem_in_base = phys_to_virt(phy_in_base); |
| |
| memcpy((void *)sharemem_in_base, |
| (const void *)arg->buffer, size); |
| } |
| |
| asm __volatile__("" : : : "memory"); |
| |
| arm_smccc_smc(cmd, offset, size, 0, 0, 0, 0, 0, &res); |
| ret = res.a0; |
| *retcnt = res.a0; |
| |
| if (arg->cmd == EFUSE_HAL_API_WRITE) { |
| if (!pfn_valid(__phys_to_pfn(phy_in_base))) |
| iounmap(sharemem_in_base); |
| } else if ((arg->cmd == EFUSE_HAL_API_READ) && (ret != 0)) { |
| phy_out_base = get_sharemem_info(efuse_cmd.mem_out_base_cmd); |
| |
| if (!pfn_valid(__phys_to_pfn(phy_out_base))) |
| sharemem_out_base = ioremap_nocache(phy_out_base, ret); |
| else |
| sharemem_out_base = phys_to_virt(phy_out_base); |
| |
| memcpy((void *)arg->buffer, |
| (const void *)sharemem_out_base, ret); |
| |
| if (!pfn_valid(__phys_to_pfn(phy_out_base))) |
| iounmap(sharemem_out_base); |
| } |
| |
| mutex_unlock(&efuse_lock); |
| |
| if (!ret) |
| return -1; |
| |
| return 0; |
| } |
| |
| static ssize_t meson_trustzone_efuse(struct efuse_hal_api_arg *arg) |
| { |
| ssize_t ret; |
| struct cpumask task_cpumask; |
| |
| if (!arg) |
| return -1; |
| |
| cpumask_copy(&task_cpumask, current->cpus_ptr); |
| set_cpus_allowed_ptr(current, cpumask_of(0)); |
| |
| ret = meson_efuse_fn_smc(arg); |
| set_cpus_allowed_ptr(current, &task_cpumask); |
| |
| return ret; |
| } |
| |
| static unsigned long efuse_data_process(unsigned long type, |
| unsigned long buffer, |
| unsigned long length, |
| unsigned long option) |
| { |
| struct arm_smccc_res res; |
| void *sharemem_in_base; |
| long phy_in_base; |
| struct page *page; |
| |
| mutex_lock(&efuse_lock); |
| |
| phy_in_base = get_sharemem_info(efuse_cmd.mem_in_base_cmd); |
| page = pfn_to_page(phy_in_base >> PAGE_SHIFT); |
| |
| if (!pfn_valid(__phys_to_pfn(phy_in_base))) |
| sharemem_in_base = ioremap_nocache(phy_in_base, length); |
| else |
| sharemem_in_base = phys_to_virt(phy_in_base); |
| |
| memcpy((void *)sharemem_in_base, |
| (const void *)buffer, length); |
| |
| asm __volatile__("" : : : "memory"); |
| |
| do { |
| arm_smccc_smc((unsigned long)AML_DATA_PROCESS, |
| (unsigned long)type, |
| (unsigned long)phy_in_base, |
| (unsigned long)length, |
| (unsigned long)option, |
| 0, 0, 0, &res); |
| } while (0); |
| |
| if (!pfn_valid(__phys_to_pfn(phy_in_base))) |
| iounmap(sharemem_in_base); |
| |
| mutex_unlock(&efuse_lock); |
| |
| return res.a0; |
| } |
| |
| int efuse_amlogic_cali_item_read(unsigned int item) |
| { |
| struct arm_smccc_res res; |
| |
| /* range check */ |
| if (item < EFUSE_CALI_SUBITEM_WHOBURN || |
| item > EFUSE_CALI_SUBITEM_BC) |
| return -EINVAL; |
| |
| mutex_lock(&efuse_lock); |
| |
| do { |
| arm_smccc_smc((unsigned long)EFUSE_READ_CALI_ITEM, |
| (unsigned long)item, |
| 0, 0, 0, 0, 0, 0, &res); |
| } while (0); |
| |
| mutex_unlock(&efuse_lock); |
| return res.a0; |
| } |
| EXPORT_SYMBOL_GPL(efuse_amlogic_cali_item_read); |
| |
| /* |
| *retrun: 1: wrote, 0: not write, -1: fail or not support |
| */ |
| int efuse_amlogic_check_lockable_item(unsigned int item) |
| { |
| struct arm_smccc_res res; |
| |
| /* range check */ |
| if (item < EFUSE_LOCK_SUBITEM_BASE || |
| item > EFUSE_LOCK_SUBITEM_MAX) |
| return -EINVAL; |
| |
| mutex_lock(&efuse_lock); |
| |
| do { |
| arm_smccc_smc((unsigned long)EFUSE_READ_CALI_ITEM, |
| (unsigned long)item, |
| 0, 0, 0, 0, 0, 0, &res); |
| } while (0); |
| |
| mutex_unlock(&efuse_lock); |
| return res.a0; |
| } |
| EXPORT_SYMBOL_GPL(efuse_amlogic_check_lockable_item); |
| |
| unsigned long efuse_amlogic_set(char *buf, size_t count) |
| { |
| unsigned long ret; |
| struct cpumask task_cpumask; |
| |
| cpumask_copy(&task_cpumask, current->cpus_ptr); |
| set_cpus_allowed_ptr(current, cpumask_of(0)); |
| |
| ret = efuse_data_process(AML_D_P_W_EFUSE_AMLOGIC, |
| (unsigned long)buf, (unsigned long)count, 0); |
| set_cpus_allowed_ptr(current, &task_cpumask); |
| |
| return ret; |
| } |
| |
| static ssize_t meson_trustzone_efuse_get_max(struct efuse_hal_api_arg *arg) |
| { |
| ssize_t ret; |
| unsigned int cmd; |
| struct arm_smccc_res res; |
| |
| if (arg->cmd != EFUSE_HAL_API_USER_MAX) |
| return -1; |
| |
| cmd = efuse_cmd.get_max_cmd; |
| |
| asm __volatile__("" : : : "memory"); |
| arm_smccc_smc(cmd, 0, 0, 0, 0, 0, 0, 0, &res); |
| ret = res.a0; |
| |
| if (!ret) |
| return -1; |
| |
| return ret; |
| } |
| |
| ssize_t efuse_get_max(void) |
| { |
| struct efuse_hal_api_arg arg; |
| ssize_t ret; |
| struct cpumask task_cpumask; |
| |
| arg.cmd = EFUSE_HAL_API_USER_MAX; |
| |
| cpumask_copy(&task_cpumask, current->cpus_ptr); |
| set_cpus_allowed_ptr(current, cpumask_of(0)); |
| |
| ret = meson_trustzone_efuse_get_max(&arg); |
| set_cpus_allowed_ptr(current, &task_cpumask); |
| |
| return ret; |
| } |
| |
| static ssize_t _efuse_read(char *buf, size_t count, loff_t *ppos) |
| { |
| unsigned int pos = *ppos; |
| |
| struct efuse_hal_api_arg arg; |
| unsigned int retcnt; |
| ssize_t ret; |
| |
| arg.cmd = EFUSE_HAL_API_READ; |
| arg.offset = pos; |
| arg.size = count; |
| arg.buffer = (unsigned long)buf; |
| arg.retcnt = (unsigned long)&retcnt; |
| ret = meson_trustzone_efuse(&arg); |
| if (ret == 0) { |
| *ppos += retcnt; |
| return retcnt; |
| } |
| |
| return ret; |
| } |
| |
| static ssize_t _efuse_write(const char *buf, size_t count, loff_t *ppos) |
| { |
| unsigned int pos = *ppos; |
| |
| struct efuse_hal_api_arg arg; |
| unsigned int retcnt; |
| ssize_t ret; |
| |
| arg.cmd = EFUSE_HAL_API_WRITE; |
| arg.offset = pos; |
| arg.size = count; |
| arg.buffer = (unsigned long)buf; |
| arg.retcnt = (unsigned long)&retcnt; |
| |
| ret = meson_trustzone_efuse(&arg); |
| if (ret == 0) { |
| *ppos = retcnt; |
| return retcnt; |
| } |
| |
| return ret; |
| } |
| |
| ssize_t efuse_read_usr(char *buf, size_t count, loff_t *ppos) |
| { |
| char *pdata = NULL; |
| ssize_t ret; |
| loff_t pos; |
| |
| pdata = kmalloc(count, GFP_KERNEL); |
| if (!pdata) |
| return -ENOMEM; |
| |
| pos = *ppos; |
| |
| ret = _efuse_read(pdata, count, (loff_t *)&pos); |
| |
| memcpy(buf, pdata, count); |
| kfree(pdata); |
| |
| return ret; |
| } |
| |
| ssize_t efuse_write_usr(char *buf, size_t count, loff_t *ppos) |
| { |
| char *pdata = NULL; |
| ssize_t ret; |
| loff_t pos; |
| |
| pdata = kmalloc(count, GFP_KERNEL); |
| if (!pdata) |
| return -ENOMEM; |
| |
| memcpy(pdata, buf, count); |
| pos = *ppos; |
| |
| ret = _efuse_write(pdata, count, (loff_t *)&pos); |
| kfree(pdata); |
| |
| return ret; |
| } |