Files
Linux_Drivers/linux_5.10/drivers/tee/tee_cv_private.c
wangliang.wang 4f810186ab [linux] porting cvitek asic chips.
1. update cv182x/cv183x configuration file
	2. update cv181x/cv180x configuration file
	3. update clk driver for cvitek
	4. update dma driver for cvitek
	5. update soc driver for cvitek
	6. porting cvitek ion driver from kernel-4.19
	7. compatible with riscv

Change-Id: Icff9fafe0ebe7d6bab824bbadb952e08bdc66c19
2023-03-10 20:33:10 +08:00

630 lines
15 KiB
C

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/buffer_head.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/ioctl.h>
#include <linux/kdev_t.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/tee_drv.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include <linux/arm-smccc.h>
#include <asm/cacheflush.h>
#include <linux/kernel_read_file.h>
#include <linux/uaccess.h>
#include <linux/clk.h>
#include "tee_cv_private.h"
#define TEE_CV_DEBUG 0
#ifdef CONFIG_ARM
#define FLUSH_DCACHE __cpuc_flush_dcache_area
#else
#define FLUSH_DCACHE __flush_dcache_area
#endif
static struct clk *clk_efuse;
static char aimodel_keyfile_path[PAGE_SIZE] = "/mnt/data/aimodel.keys";
static char debug_info[PAGE_SIZE] = "(nothing)";
#if TEE_CV_DEBUG
/*
* /memreserve/ 0x0000000138000000 0x0000000008000000; // TEE buffer 896MB ~ 1024MB (128MB)
*/
static phys_addr_t large_buf_phys = 0x0000000138000000;
static const size_t large_buf_maxsize = 0x04000000;
static size_t large_buf_size;
static void *large_buf;
#endif
static int64_t plat_efuse_read_safe(uint32_t addr);
static int plat_efuse_write_safe(uint32_t addr, uint32_t value);
static inline void mmio_write_32(void __iomem *addr, uint32_t value)
{
iowrite32(value, addr);
}
static inline void mmio_setbits_32(void __iomem *addr, uint32_t set)
{
mmio_write_32(addr, ioread32(addr) | set);
}
static void do_smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2,
unsigned long a3, unsigned long a4, unsigned long a5,
unsigned long a6, unsigned long a7,
struct arm_smccc_res *res)
{
int err;
err = clk_prepare_enable(clk_efuse);
if (err) {
pr_err("clk_efuse: clock failed to prepare+enable: %d\n", err);
return;
}
pr_info("do_smccc_smc: a=0x%lx 0x%lx 0x%lx\n", a0, a1, a2);
arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res);
pr_info("do_smccc_smc: r=0x%lx 0x%lx\n", res->a0, res->a1);
clk_disable_unprepare(clk_efuse);
}
int tee_cv_aimodel_keyfile_send(const char *path)
{
int err = 0;
struct arm_smccc_res res = { 0 };
void *key_buf = 0;
void *key_cont = 0;
phys_addr_t key_cont_phy = 0;
size_t key_size = 0;
pr_info("aimodel_keyfile_path=%s\n", path);
err = kernel_read_file_from_path(aimodel_keyfile_path, 0, &key_buf,
TEE_TPU_MAX_KEYFILE_SIZE,
&key_size, READING_FIRMWARE);
if (err < 0)
return err;
// Copy to contiguous physical memory
key_cont = kzalloc(key_size, GFP_KERNEL);
if (!key_cont) {
vfree(key_buf);
return -ENOMEM;
}
memcpy(key_cont, key_buf, key_size);
vfree(key_buf);
key_cont_phy = virt_to_phys(key_cont);
FLUSH_DCACHE(key_cont, key_size);
pr_info("key_size=%zu %pa |%16ph|\n", key_size, &key_cont_phy,
key_cont);
do_smccc_smc(OPTEE_SMC_CALL_CV_TPU_SEND_KEYS, key_cont_phy, key_size, 0,
0, 0, 0, 0, &res);
kfree(key_cont);
return res.a0;
}
EXPORT_SYMBOL(tee_cv_aimodel_keyfile_send);
/*
* Flush cmd_phys and weight_phys before calling
*/
int tee_cv_aimodel_load(phys_addr_t cmd_phys, size_t cmd_size,
phys_addr_t weight_phys, size_t weight_size,
phys_addr_t neuron_phys, bool decrypt_cmd,
bool decrypt_weight, struct arm_smccc_res *res)
{
uint32_t flag = 0;
pr_info("%s(): cmd_phys=0x%llx cmd_size=%zu weight_phys=0x%llx weight_size=%zu neuron_phys=0x%llx\n",
__func__, cmd_phys, cmd_size, weight_phys, weight_size,
neuron_phys);
if (decrypt_cmd)
flag |= CV_DECRYPT_CMD;
if (decrypt_weight)
flag |= CV_DECRYPT_WEIGHT;
tee_cv_aimodel_keyfile_send(aimodel_keyfile_path);
do_smccc_smc(OPTEE_SMC_CALL_CV_TPU_LOAD_MODEL, cmd_phys, cmd_size,
weight_phys, weight_size, neuron_phys, flag, 0, res);
return res->a0;
}
EXPORT_SYMBOL(tee_cv_aimodel_load);
int tee_cv_aimodel_run(phys_addr_t dmabuf_phys, phys_addr_t gaddr_base2,
phys_addr_t gaddr_base3, phys_addr_t gaddr_base4,
phys_addr_t gaddr_base5, phys_addr_t gaddr_base6,
phys_addr_t gaddr_base7, struct arm_smccc_res *res)
{
pr_info("%s():\n", __func__);
do_smccc_smc(OPTEE_SMC_CALL_CV_TPU_RUN, dmabuf_phys, gaddr_base2,
gaddr_base3, gaddr_base4, gaddr_base5, gaddr_base6, gaddr_base7, res);
return res->a0;
}
EXPORT_SYMBOL(tee_cv_aimodel_run);
int tee_cv_aimodel_wait(uint64_t arg, struct arm_smccc_res *res)
{
pr_info("%s():\n", __func__);
do_smccc_smc(OPTEE_SMC_CALL_CV_TPU_WAIT, arg, 0, 0, 0, 0, 0, 0, res);
return res->a0;
}
EXPORT_SYMBOL(tee_cv_aimodel_wait);
int tee_cv_aimodel_set_prot_range(phys_addr_t addr, uint32_t size, struct arm_smccc_res *res)
{
pr_info("%s(): addr=0x%llx size=0x%x\n", __func__, addr, size);
do_smccc_smc(OPTEE_SMC_CALL_CV_TPU_SET_PROT_RANGE, addr, size, 0, 0, 0, 0, 0, res);
return res->a0;
}
EXPORT_SYMBOL(tee_cv_aimodel_set_prot_range);
int tee_cv_aimodel_clear_prot_range(phys_addr_t addr, uint32_t size, struct arm_smccc_res *res)
{
pr_info("%s(): addr=0x%llx size=0x%x\n", __func__, addr, size);
do_smccc_smc(OPTEE_SMC_CALL_CV_TPU_CLEAR_PROT_RANGE, addr, size, 0, 0, 0, 0, 0, res);
return res->a0;
}
EXPORT_SYMBOL(tee_cv_aimodel_clear_prot_range);
static ssize_t aimodel_keyfile_path_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%s\n", aimodel_keyfile_path);
}
static ssize_t aimodel_keyfile_path_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
char *p = 0;
strlcpy(aimodel_keyfile_path, buf, PAGE_SIZE - 1);
p = strpbrk(aimodel_keyfile_path, "\n\r");
if (p)
*p = '\0';
tee_cv_aimodel_keyfile_send(aimodel_keyfile_path);
return count;
}
#if TEE_CV_DEBUG
static int read_file(char *path, void *buf, size_t *size)
{
int err = 0;
void *vbuf;
loff_t read_size = 0;
err = kernel_read_file_from_path(path, &vbuf, &read_size,
large_buf_size, READING_FIRMWARE);
if (err < 0)
return err;
if (read_size <= *size)
*size = read_size;
memcpy(buf, vbuf, *size);
vfree(vbuf);
return err;
}
static void tpu_load_debug(void)
{
void *cmd, *weight;
size_t cmd_size, weight_size;
struct arm_smccc_res res = { 0 };
pr_info("tpu_load_debug:\n");
large_buf = memremap(large_buf_phys, large_buf_maxsize, MEMREMAP_WB);
if (!large_buf) {
pr_err("[%s] memremap fail.\n", __func__);
return;
}
large_buf_size = large_buf_maxsize;
cmd = large_buf;
cmd_size = large_buf_size;
read_file("/mnt/data/cmd.bin.enc", cmd, &cmd_size);
pr_info("cmd=%p(%lu) |%32ph|\n", cmd, cmd_size, cmd);
large_buf += roundup(cmd_size, 16);
large_buf_size -= roundup(cmd_size, 16);
weight = large_buf;
weight_size = large_buf_size;
read_file("/mnt/data/weight.bin.enc", weight, &weight_size);
pr_info("weight=%p(%lu) |%32ph|\n", weight, weight_size, weight);
FLUSH_DCACHE(cmd, cmd_size);
FLUSH_DCACHE(weight, weight_size);
tee_cv_aimodel_load(virt_to_phys(cmd), cmd_size, virt_to_phys(weight),
weight_size, virt_to_phys(weight), &res);
}
static void tpu_set_prot_debug(void)
{
phys_addr_t cmd, weight;
size_t cmd_size, weight_size;
struct arm_smccc_res res = { 0 };
pr_info("%s()\n", __func__);
cmd = large_buf_phys;
cmd_size = 0x1000;
weight = cmd + roundup(cmd_size, 16);
weight_size = 0x1400000;
tee_cv_aimodel_set_prot_range(weight, weight_size, &res);
}
static void tpu_clear_prot_debug(void)
{
phys_addr_t cmd, weight;
size_t cmd_size, weight_size;
struct arm_smccc_res res = { 0 };
pr_info("%s()\n", __func__);
cmd = large_buf_phys;
cmd_size = 0x1000;
weight = cmd + roundup(cmd_size, 16);
weight_size = 0x2000;
tee_cv_aimodel_clear_prot_range(weight, weight_size, &res);
}
#else /* TEE_CV_DEBUG */
static void tpu_load_debug(void)
{
}
static void tpu_clear_prot_debug(void)
{
}
static void tpu_set_prot_debug(void)
{
}
#endif /* TEE_CV_DEBUG */
static ssize_t cv_debug_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%s\n", debug_info);
}
static ssize_t cv_debug_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
char *p = 0;
struct arm_smccc_res res = { 0 };
phys_addr_t debug_info_phy = 0;
memset(debug_info, 0, sizeof(debug_info));
memcpy(debug_info, buf, count);
debug_info[sizeof(debug_info) - 1] = '\0';
p = strpbrk(debug_info, "\n\r");
if (p)
*p = '\0';
pr_info("cv_debug_info=0x%09llx(%zu) |%16ph|\n", debug_info_phy,
sizeof(debug_info), debug_info);
if (strncmp("tpu_load", debug_info, sizeof(debug_info)) == 0) {
tpu_load_debug();
} else if (strncmp("tpu_set_prot", debug_info, sizeof(debug_info)) == 0) {
tpu_set_prot_debug();
} else if (strncmp("tpu_clear_prot", debug_info, sizeof(debug_info)) == 0) {
tpu_clear_prot_debug();
} else if (strncmp("console_on", debug_info, sizeof(debug_info)) == 0) {
pr_info("Enable BL32 console output\n");
do_smccc_smc(OPTEE_SMC_CALL_CV_DEBUG, 0x7F000001, 2, 0, 0, 0, 0, 0, &res);
} else {
debug_info_phy = virt_to_phys(debug_info);
FLUSH_DCACHE(debug_info, sizeof(debug_info));
do_smccc_smc(OPTEE_SMC_CALL_CV_DEBUG, 0x1F000000,
debug_info_phy, sizeof(debug_info), 0, 0, 0, 0,
&res);
}
return count;
}
/*
* /sys/devices/virtual/tee/tee0/aimodel_keyfile_path
* /sys/devices/virtual/tee/tee0/cv_debug
*/
DEVICE_ATTR_RW(aimodel_keyfile_path);
DEVICE_ATTR_RW(cv_debug);
#define EFUSE_BASE (0x03000000 + 0x00050000)
#define EFUSE_REG_BANK_SIZE 0x1000
#define EFUSE_SIZE 0x100
#define EFUSE_SHADOW_REG (efuse_base + 0x100)
#define EFUSE_MODE (efuse_base + 0x0)
#define EFUSE_ADR (efuse_base + 0x4)
#define EFUSE_DIR_CMD (efuse_base + 0x8)
#define EFUSE_RD_DATA (efuse_base + 0xC)
#define EFUSE_STATUS (efuse_base + 0x10)
#define EFUSE_ONE_WAY (efuse_base + 0x14)
#define BIT_AREAD (1 << 0)
#define BIT_MREAD (1 << 1)
#define BIT_PRG (1 << 2)
#define BIT_PWR_DN (1 << 3)
#define BIT_CMD (1 << 4)
#define BIT_BUSY (1 << 0)
#define CMD_REFRESH (0x30)
enum EFUSE_READ_TYPE { AREAD, MREAD };
static void __iomem *efuse_base;
int tee_cv_init(void)
{
pr_info("%s\n", __func__);
clk_efuse = clk_get_sys(NULL, "clk_efuse");
if (IS_ERR(clk_efuse)) {
pr_err("clk_efuse: clock not found %ld\n", PTR_ERR(clk_efuse));
return PTR_ERR(clk_efuse);
}
efuse_base = ioremap(EFUSE_BASE, EFUSE_REG_BANK_SIZE);
return 0;
}
void tee_cv_exit(void)
{
iounmap(efuse_base);
}
int tee_cv_efuse_read(uint32_t addr, size_t size, void *buf)
{
long ret = -1;
struct arm_smccc_res res = { 0 };
void *efuse_buf;
phys_addr_t efuse_buf_phys;
size_t i;
uint32_t v;
if (addr % 4 || size % 4 || !size)
return -EFAULT;
efuse_buf = kzalloc(size, GFP_KERNEL);
if (!efuse_buf)
return -ENOMEM;
FLUSH_DCACHE(efuse_buf, size);
efuse_buf_phys = virt_to_phys(efuse_buf);
pr_debug("%s(): 0x%x(%zu) to %p(0x%llx)\n", __func__, addr, size,
efuse_buf, efuse_buf_phys);
do_smccc_smc(OPTEE_SMC_CALL_CV_EFUSE_READ, addr, size, efuse_buf_phys,
0, 0, 0, 0, &res);
ret = (typeof(ret))res.a0;
pr_debug("%s(): ret=0x%lx\n", __func__, ret);
if (ret >= 0)
memcpy(buf, efuse_buf, min((typeof(res.a0))size, res.a0));
kfree(efuse_buf);
if (ret < 0) {
for (i = 0; i < size && (addr + i) < EFUSE_SIZE; i += 4) {
v = plat_efuse_read_safe(addr + i);
memcpy(buf + i, &v, sizeof(v));
}
return i;
}
return ret;
}
EXPORT_SYMBOL(tee_cv_efuse_read);
int tee_cv_efuse_write(uint32_t addr, uint32_t value)
{
long ret = -1;
struct arm_smccc_res res = { 0 };
pr_debug("%s(): 0x%4x=0x%08x\n", __func__, addr, value);
do_smccc_smc(OPTEE_SMC_CALL_CV_EFUSE_WRITE, addr, value, 0, 0, 0, 0, 0,
&res);
ret = (typeof(ret))res.a0;
if (ret < 0)
return plat_efuse_write_safe(addr, value);
return ret;
}
EXPORT_SYMBOL(tee_cv_efuse_write);
static int64_t plat_efuse_read_safe(uint32_t addr)
{
int err;
uint32_t v;
if (addr >= EFUSE_SIZE)
return -EFAULT;
if (addr % 4 != 0)
return -EFAULT;
err = clk_prepare_enable(clk_efuse);
if (err) {
pr_err("clk_efuse: clock failed to prepare+enable: %d\n", err);
return -EIO;
}
v = ioread32(EFUSE_SHADOW_REG + addr);
clk_disable_unprepare(clk_efuse);
return v;
}
static void efuse_wait_for_ready(void)
{
while (ioread32(EFUSE_STATUS) & BIT_BUSY)
;
}
static void efuse_power_on(uint32_t on)
{
if (on)
mmio_setbits_32(EFUSE_MODE, BIT_CMD);
else
mmio_setbits_32(EFUSE_MODE, BIT_PWR_DN | BIT_CMD);
}
static void efuse_refresh(void)
{
mmio_write_32(EFUSE_MODE, CMD_REFRESH);
}
static void efuse_prog_bit(uint32_t word_addr, uint32_t bit_addr,
uint32_t high_row)
{
uint32_t phy_addr;
// word_addr: virtual addr, take "lower 6-bits" from 7-bits (0-127)
// bit_addr: virtual addr, 5-bits (0-31)
// composite physical addr[11:0] = [11:7]bit_addr + [6:0]word_addr
phy_addr =
((bit_addr & 0x1F) << 7) | ((word_addr & 0x3F) << 1) | high_row;
efuse_wait_for_ready();
// send efuse program cmd
mmio_write_32(EFUSE_ADR, phy_addr);
mmio_write_32(EFUSE_MODE, BIT_PRG | BIT_CMD);
}
static uint32_t efuse_read_word(uint32_t phy_word_addr,
enum EFUSE_READ_TYPE type)
{
// power on efuse macro
efuse_power_on(1);
efuse_wait_for_ready();
mmio_write_32(EFUSE_ADR, phy_word_addr);
if (type == AREAD) // array read
mmio_write_32(EFUSE_MODE, BIT_AREAD | BIT_CMD);
else if (type == MREAD) // margin read
mmio_write_32(EFUSE_MODE, BIT_MREAD | BIT_CMD);
else
pr_err("EFUSE: Unsupported read type!");
efuse_wait_for_ready();
return ioread32(EFUSE_RD_DATA);
}
static int plat_bm_efuse_write(uint32_t vir_word_addr, uint32_t val)
{
uint32_t i, j, row_val, zero_bit;
uint32_t new_value;
int err_cnt = 0;
for (j = 0; j < 2; j++) {
pr_debug("EFUSE: Program physical word addr #%d\n",
(vir_word_addr << 1) | j);
// array read by word address
row_val = efuse_read_word((vir_word_addr << 1) | j,
AREAD); // read low word of word_addr
zero_bit = val & (~row_val); // only program zero bit
// program row which bit is zero
for (i = 0; i < 32; i++) {
if ((zero_bit >> i) & 1)
efuse_prog_bit(vir_word_addr, i, j);
}
// check by margin read
new_value = efuse_read_word((vir_word_addr << 1) | j, MREAD);
pr_debug("%s(): val=0x%x new_value=0x%x\n", __func__, val,
new_value);
if ((val & new_value) != val) {
err_cnt += 1;
pr_err("EFUSE: Program bits check failed (%d)!\n",
err_cnt);
}
}
efuse_refresh();
return err_cnt >= 2 ? -EIO : 0;
}
static void plat_bm_efuse_init(void)
{
// power on efuse macro
efuse_power_on(1);
// send refresh cmd to reload all eFuse values to shadow registers
efuse_refresh();
// efuse macro will be auto powered off after refresh cmd, so don't
// need to turn it off manually
}
static int plat_efuse_write_safe(uint32_t addr, uint32_t value)
{
int ret;
pr_debug("%s(): 0x%x = 0x%x\n", __func__, addr, value);
if (addr >= EFUSE_SIZE)
return -EFAULT;
if (addr % 4 != 0)
return -EFAULT;
ret = clk_prepare_enable(clk_efuse);
if (ret) {
pr_err("clk_efuse: clock failed to prepare+enable: %d\n", ret);
return -EIO;
}
ret = plat_bm_efuse_write(addr / 4, value);
pr_debug("%s(): ret=%d\n", __func__, ret);
plat_bm_efuse_init();
efuse_wait_for_ready();
clk_disable_unprepare(clk_efuse);
return ret;
}