Commit 7d91f398 authored by zhangshuowen96's avatar zhangshuowen96
Browse files

drivers: misc: sdma-dae: support sqe task send and execute

kunpeng inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I9W355


CVE: NA

----------------------------------------------------------------------

Add ioctl for user to operate registers abourt sq/cq
head and tail. Support sending task info to kernel, and
add/check authorities.

Signed-off-by: default avatarzhangshuowen96 <zhangshuowen@hisilicon.com>
parent f8eeb398
Loading
Loading
Loading
Loading
+58 −2
Original line number Diff line number Diff line
@@ -28,9 +28,15 @@
#define HISI_SDMA_CQ_SIZE			(HISI_SDMA_CQ_ENTRY_SIZE * HISI_SDMA_CQ_LENGTH)
#define HISI_SDMA_REG_SIZE			4096
#define HISI_SDMA_CH_OFFSET			(HISI_STARS_CHN_NUM * HISI_SDMA_REG_SIZE)
#define HISI_SDMA_BYPASS_GROUP_MEMBER		32
#define HISI_SDMA_DEVICE_NAME_MAX		20
#define HISI_SDMA_READ_REG			1
#define HISI_SDMA_WRITE_REG			2
#define HISI_SDMA_MAX_ALLOC_SIZE		0x400000

#define HISI_SDMA_CLR_NORMAL_SQE_CNT		1
#define HISI_SDMA_CLR_ERR_SQE_CNT		2

struct chn_ioe_info {
	u32 ch_err_status;
	u32 ch_cqe_sqeid;
@@ -112,6 +118,46 @@ struct hisi_sdma_share_chn {
	bool   init_flag;
};

struct hisi_sdma_reg_info {
	int chn;
	int type;
	u32 reg_value;
};

struct hisi_sdma_pid_info {
	int num;
	uintptr_t pid_list_addr;
};

typedef void (*sdma_task_callback)(int task_status, void *task_data);

struct hisi_sdma_sqe_task {
	u64 src_addr;
	u64 dst_addr;
	u32 src_process_id;
	u32 dst_process_id;
	u32 src_stride_len;
	u32 dst_stride_len;
	u32 stride_num;
	u32 length;
	u8 opcode;
	u8 mpam_partid;
	u8 pmg : 2;
	u8 resvd1 : 6;
	u8 qos : 4;
	u8 resvd2 : 4;
	sdma_task_callback task_cb;
	void *task_data;
	struct sdma_sqe_task *next_sqe;
};

struct hisi_sdma_task_info {
	int chn;
	u32 req_cnt;
	u32 task_cnt;
	uintptr_t task_addr;
};

typedef int (*sdma_ioctl_funcs)(struct file *file, unsigned long arg);
struct hisi_sdma_ioctl_func_list {
	unsigned int cmd;
@@ -122,7 +168,17 @@ struct hisi_sdma_ioctl_func_list {
#define IOCTL_SDMA_GET_CHN		_IOR('s', 2, int)
#define IOCTL_SDMA_PUT_CHN		_IOW('s', 3, int)
#define IOCTL_SDMA_GET_STREAMID		_IOR('s', 4, u32)
#define IOCTL_GET_SDMA_CHN_NUM		_IOR('s', 5, struct hisi_sdma_chn_num)
#define IOCTL_SDMA_CHN_USED_REFCOUNT	_IOW('s', 6, struct hisi_sdma_share_chn)
#define IOCTL_GET_SDMA_NUM		_IOR('s', 7, int)
#define IOCTL_GET_NEAR_SDMAID		_IOR('s', 8, int)
#define IOCTL_GET_SDMA_CHN_NUM		_IOR('s', 9, struct hisi_sdma_chn_num)
#define IOCTL_SDMA_CHN_USED_REFCOUNT	_IOW('s', 11, struct hisi_sdma_share_chn)
#define IOCTL_SDMA_ADD_AUTH_HT		_IOW('s', 12, struct hisi_sdma_pid_info)
#define IOCTL_SDMA_SEND_TASK		_IOWR('s', 13, struct hisi_sdma_task_info)
#define IOCTL_SDMA_SQ_HEAD_REG		_IOWR('s', 14, struct hisi_sdma_reg_info)
#define IOCTL_SDMA_SQ_TAIL_REG		_IOWR('s', 15, struct hisi_sdma_reg_info)
#define IOCTL_SDMA_CQ_HEAD_REG		_IOWR('s', 16, struct hisi_sdma_reg_info)
#define IOCTL_SDMA_CQ_TAIL_REG		_IOWR('s', 17, struct hisi_sdma_reg_info)
#define IOCTL_SDMA_DFX_REG		_IOWR('s', 18, struct hisi_sdma_reg_info)
#define IOCTL_SDMA_SQE_CNT_REG		_IOW('s', 19, struct hisi_sdma_reg_info)

#endif
+209 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
#include <linux/hashtable.h>
#include <linux/pagemap.h>
#include <linux/slab.h>
#include "hisi_sdma.h"
#include "sdma_auth.h"

#define HISI_SDMA_AUTH_HASH_BUCKETS_BITS 10

struct hisi_sdma_sub_pid_hte {
	u32 pid;
	struct hlist_node pnode;
};

struct hisi_sdma_own_pid_hte {
	u32 pid;
	u32 pasid;
	struct hlist_node node;
	DECLARE_HASHTABLE(sdma_submitter_pid_ht, HISI_SDMA_AUTH_HASH_BUCKETS_BITS);
	spinlock_t submitter_pid_lock;
};

struct hisi_sdma_auth_ht {
	DECLARE_HASHTABLE(sdma_owner_pid_ht, HISI_SDMA_AUTH_HASH_BUCKETS_BITS);
	spinlock_t owner_pid_lock;
};

static struct hisi_sdma_auth_ht *g_authority;

int sdma_authority_hash_init(void)
{
	g_authority = kmalloc(sizeof(struct hisi_sdma_auth_ht), GFP_KERNEL);
	if (!g_authority)
		return -ENOMEM;

	hash_init(g_authority->sdma_owner_pid_ht);
	spin_lock_init(&g_authority->owner_pid_lock);

	return 0;
}

static void entry_free_pid_ht(struct hisi_sdma_own_pid_hte *entry)
{
	struct hisi_sdma_sub_pid_hte *sub_entry;
	struct hlist_node *tmp;
	u32 bkt;

	spin_lock(&entry->submitter_pid_lock);
	hash_for_each_safe(entry->sdma_submitter_pid_ht, bkt, tmp, sub_entry, pnode) {
		hash_del(&sub_entry->pnode);
		kfree(sub_entry);
		sub_entry = NULL;
	}
	spin_unlock(&entry->submitter_pid_lock);
	hash_del(&entry->node);
	kfree(entry);
}

void sdma_authority_ht_free(void)
{
	struct hisi_sdma_own_pid_hte *entry;
	struct hlist_node *tmp;
	u32 bkt;

	spin_lock(&g_authority->owner_pid_lock);
	hash_for_each_safe(g_authority->sdma_owner_pid_ht, bkt, tmp, entry, node)
		entry_free_pid_ht(entry);

	spin_unlock(&g_authority->owner_pid_lock);

	kfree(g_authority);
	g_authority = NULL;
}

static struct hisi_sdma_sub_pid_hte *sdma_search_submitter_pid(struct hisi_sdma_own_pid_hte *entry,
							       u32 submitter_pid)
{
	struct hisi_sdma_sub_pid_hte *sub_entry;

	hash_for_each_possible(entry->sdma_submitter_pid_ht, sub_entry, pnode, submitter_pid)
		if (sub_entry->pid == submitter_pid)
			return sub_entry;

	return NULL;
}

void sdma_free_authority_ht_with_pid(u32 pid)
{
	struct hisi_sdma_sub_pid_hte *sub_entry;
	struct hisi_sdma_own_pid_hte *entry;
	struct hlist_node *tmp;
	u32 bkt;

	spin_lock(&g_authority->owner_pid_lock);
	hash_for_each_safe(g_authority->sdma_owner_pid_ht, bkt, tmp, entry, node) {
		if (entry->pid == pid)
			entry_free_pid_ht(entry);
		else {
			spin_lock(&entry->submitter_pid_lock);
			sub_entry = sdma_search_submitter_pid(entry, pid);
			if (sub_entry) {
				hash_del(&sub_entry->pnode);
				kfree(sub_entry);
				sub_entry = NULL;
			}
			spin_unlock(&entry->submitter_pid_lock);
		}
	}
	spin_unlock(&g_authority->owner_pid_lock);
}

static struct hisi_sdma_own_pid_hte *sdma_search_owner_pid_ht(u32 pid)
{
	struct hisi_sdma_own_pid_hte *entry;

	hash_for_each_possible(g_authority->sdma_owner_pid_ht, entry, node, pid)
		if (entry->pid == pid)
			return entry;

	return NULL;
}

static int sdma_add_authority_ht(struct hisi_sdma_own_pid_hte *entry, int count, u32 *list)
{
	struct hisi_sdma_sub_pid_hte *sub_entry;
	int i;

	for (i = 0; i < count; i++) {
		sub_entry = sdma_search_submitter_pid(entry, list[i]);
		if (sub_entry)
			continue;

		sub_entry = kzalloc(sizeof(struct hisi_sdma_sub_pid_hte), GFP_KERNEL);
		if (!sub_entry)
			return -ENOMEM;

		sub_entry->pid = list[i];
		hash_add(entry->sdma_submitter_pid_ht, &sub_entry->pnode, sub_entry->pid);
	}

	return 0;
}

static int sdma_create_authority_ht(u32 pid, u32 pasid, int num, u32 *list)
{
	struct hisi_sdma_own_pid_hte *entry;
	int ret;

	entry = kzalloc(sizeof(struct hisi_sdma_own_pid_hte), GFP_KERNEL);
	if (!entry)
		return -ENOMEM;

	entry->pid = pid;
	entry->pasid = pasid;
	spin_lock_init(&entry->submitter_pid_lock);
	hash_init(entry->sdma_submitter_pid_ht);
	ret = sdma_add_authority_ht(entry, num, list);
	hash_add(g_authority->sdma_owner_pid_ht, &entry->node, entry->pid);

	return ret;
}

int sdma_auth_add(u32 pasid, int num, u32 *pid_list)
{
	struct hisi_sdma_own_pid_hte *owner;
	u32 pid = (u32)current->tgid;
	int ret = 0;

	owner = sdma_search_owner_pid_ht(pid);
	if (owner) {
		spin_lock(&owner->submitter_pid_lock);
		ret = sdma_add_authority_ht(owner, num, pid_list);
		spin_unlock(&owner->submitter_pid_lock);
		if (ret < 0)
			pr_err("add_pid_ht failed\n");
	} else {
		spin_lock(&g_authority->owner_pid_lock);
		ret = sdma_create_authority_ht(pid, pasid, num, pid_list);
		spin_unlock(&g_authority->owner_pid_lock);
		if (ret < 0)
			pr_err("create_pid_ht failed\n");
	}

	return ret;
}

int sdma_check_authority(u32 pasid, u32 owner_pid, u32 submitter_pid, u32 *owner_pasid)
{
	struct hisi_sdma_sub_pid_hte *sub_entry;
	struct hisi_sdma_own_pid_hte *entry;

	if (owner_pid == submitter_pid) {
		*owner_pasid = pasid;
		return 0;
	}
	entry = sdma_search_owner_pid_ht(owner_pid);
	if (!entry) {
		pr_err("the owner_pid_ht[%u] not exist\n", owner_pid);
		return -ENODATA;
	}
	sub_entry = sdma_search_submitter_pid(entry, submitter_pid);
	if (!sub_entry) {
		pr_err("the submitter[%u] not authorithed\n", submitter_pid);
		return -ENODATA;
	}

	*owner_pasid = entry->pasid;
	return 0;
}
+13 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __HISI_SDMA_AUTH_H__
#define __HISI_SDMA_AUTH_H__

#include "sdma_hal.h"

int sdma_authority_hash_init(void);
void sdma_authority_ht_free(void);
void sdma_free_authority_ht_with_pid(u32 pid);
int sdma_auth_add(u32 pasid, int num, u32 *pid_list);
int sdma_check_authority(u32 pasid, u32 owner_pid, u32 submitter_pid, u32 *owner_pasid);

#endif
+375 −7
Original line number Diff line number Diff line
@@ -5,7 +5,9 @@
#include <linux/list.h>
#include <linux/platform_device.h>
#include <linux/sort.h>

#include "sdma_hal.h"
#include "sdma_auth.h"

static struct hisi_sdma_global_info g_info;

@@ -23,6 +25,16 @@ struct file_open_data {
	struct list_head share_chn_list;
};

struct hisi_sdma_numa_domain {
	int idx;
	int pxm;
};

struct pasid_info {
	u32 src_pasid;
	u32 dst_pasid;
};

static int __do_sdma_open(struct hisi_sdma_device *psdma_dev, struct file *file)
{
	struct file_open_data *data;
@@ -74,6 +86,16 @@ static int __do_sdma_open(struct hisi_sdma_device *psdma_dev, struct file *file)
	return ret;
}

static int ioctl_get_sdma_num(struct file *file, unsigned long arg)
{
	u32 num = g_info.core_dev->sdma_device_num;

	if (copy_to_user((int __user *)(uintptr_t)arg, &num, sizeof(int)))
		return -EFAULT;

	return 0;
}

static int ioctl_sdma_get_process_id(struct file *file, unsigned long arg)
{
	u32 pid = (u32)current->tgid;
@@ -114,17 +136,17 @@ static int ioctl_sdma_get_chn(struct file *file, unsigned long arg)
	idx = find_first_bit(pdev->channel_map, alloc_chn_num_max);
	if (idx != alloc_chn_num_max) {
		bitmap_clear(pdev->channel_map, idx, 1);
		pdev->nr_channel_used;
		pdev->nr_channel_used++;
	} else {
		ret = -ENOSPC;
		goto unlock;
	}

	idx = share_chns;
	idx += share_chns;
	list_node->chn_idx = (int)idx;
	list_add(&list_node->chn_list, &data->non_share_chn_list);
	pchannel = pdev->channels  idx;
	pchannel->cnt_used;
	pchannel = pdev->channels + idx;
	pchannel->cnt_used++;
	spin_unlock(&pdev->channel_lock);

	dev_dbg(&pdev->pdev->dev, "sdma get chn %u\n", idx);
@@ -184,6 +206,67 @@ static int ioctl_sdma_put_chn(struct file *file, unsigned long arg)
	return 0;
}

static int cmp(const void *a, const void *b)
{
	const struct hisi_sdma_numa_domain *x = a, *y = b;

	if (x->pxm > y->pxm)
		return -1;
	if (x->pxm < y->pxm)
		return 1;
	return 0;
}

static int ioctl_get_near_sdmaid(struct file *file, unsigned long arg)
{
	u32 num = g_info.core_dev->sdma_device_num;
	struct hisi_sdma_numa_domain *sdma_numa;
	struct hisi_sdma_device *sdma_dev;
	struct device *dev;
	int sdma_id = -1;
	int nid;
	u32 i;

	nid = numa_node_id();
	if (nid < 0) {
		pr_err("numa_node not reported!\n");
		return 0;
	}

	sdma_numa = kcalloc(num, sizeof(struct hisi_sdma_numa_domain), GFP_KERNEL);
	if (!sdma_numa)
		return -ENOMEM;

	for (i = 0; i < num; i++) {
		sdma_dev = g_info.core_dev->sdma_devices[i];
		dev = &sdma_dev->pdev->dev;
		sdma_numa[i].idx = sdma_dev->idx;
		sdma_numa[i].pxm = sdma_dev->node_idx;
		if (sdma_numa[i].pxm < 0) {
			dev_err(dev, "sdma%d PXM domain not reported!\n", sdma_numa[i].idx);
			goto ret_idx;
		}
		dev_dbg(dev, "sdma%d PXM = %d\n", sdma_numa[i].idx, sdma_numa[i].pxm);
	}

	sort(sdma_numa, num, sizeof(struct hisi_sdma_numa_domain), cmp, NULL);

	for (i = 0; i < num; i++) {
		if (nid >= sdma_numa[i].pxm) {
			sdma_id = sdma_numa[i].idx;
			goto ret_idx;
		}
	}
	pr_err("Nearest sdma not found! process nid = %d\n", nid);

ret_idx:
	kfree(sdma_numa);
	if (copy_to_user((int __user *)(uintptr_t)arg, &sdma_id, sizeof(int)))
		return -EFAULT;

	return 0;
}

static int ioctl_get_sdma_chn_num(struct file *file, unsigned long arg)
{
	struct file_open_data *data = file->private_data;
@@ -222,7 +305,7 @@ static int ioctl_sdma_chn_used_refcount(struct file *file, unsigned long arg)
	}

	spin_lock(&pdev->channel_lock);
	pchannel = pdev->channels  share_chn.chn_idx;
	pchannel = pdev->channels + share_chn.chn_idx;
	if (share_chn.init_flag) {
		list_node = kmalloc_node(sizeof(struct hisi_sdma_channel_list), GFP_KERNEL,
					 pdev->node_idx);
@@ -232,7 +315,7 @@ static int ioctl_sdma_chn_used_refcount(struct file *file, unsigned long arg)
		}
		list_node->chn_idx = share_chn.chn_idx;
		list_add(&list_node->chn_list, &data->share_chn_list);
		pchannel->cnt_used;
		pchannel->cnt_used++;
	}
	if (!share_chn.init_flag && pchannel->cnt_used > 0) {
		list_for_each_entry_safe(c, n, &data->share_chn_list, chn_list) {
@@ -249,13 +332,298 @@ static int ioctl_sdma_chn_used_refcount(struct file *file, unsigned long arg)
	return 0;
}

static int ioctl_sdma_add_authority_ht(struct file *file, unsigned long arg)
{
	struct file_open_data *data = file->private_data;
	struct hisi_sdma_device *pdev = data->psdma_dev;
	struct hisi_sdma_pid_info pid_info;
	u32 *pid_list = NULL;
	int list_num;
	int ret;

	if (copy_from_user(&pid_info, (struct hisi_sdma_pid_info __user *)(uintptr_t)arg,
			   sizeof(struct hisi_sdma_pid_info))) {
		dev_err(&pdev->pdev->dev, "get hisi_sdma_pid_info failed\n");
		return -EFAULT;
	}
	list_num = pid_info.num;
	pid_list = kmalloc_node(list_num * sizeof(int), GFP_KERNEL, pdev->node_idx);
	if (!pid_list)
		return -ENOMEM;

	if (copy_from_user(pid_list, (void __user *)pid_info.pid_list_addr,
			   list_num * sizeof(u32))) {
		dev_err(&pdev->pdev->dev, "get pid_list failed\n");
		ret = -EFAULT;
		goto free_list;
	}
	ret = sdma_auth_add(data->pasid, list_num, pid_list);

free_list:
	kfree(pid_list);
	return ret;
}

static int sdma_verify_src_dst(struct file_open_data *data, struct pasid_info *pasid,
			       struct hisi_sdma_sqe_task task_list)
{
	struct device *dev = &data->psdma_dev->pdev->dev;
	u32 pid = (u32)current->tgid;
	int ret = -1;

	if (task_list.opcode == HISI_SDMA_HBM_CACHE_PRELOAD_MODE) {
		pasid->src_pasid = data->pasid;
		pasid->dst_pasid = 0;
		dev_dbg(dev, "unter hbm cach preload mode\n");
		return 0;
	}

	if (pid == task_list.src_process_id) {
		pasid->src_pasid = data->pasid;
		ret = sdma_check_authority(pasid->src_pasid, task_list.dst_process_id,
					   current->tgid, &pasid->dst_pasid);
	} else if (pid == task_list.dst_process_id) {
		pasid->dst_pasid = data->pasid;
		ret = sdma_check_authority(pasid->dst_pasid, task_list.src_process_id,
					   current->tgid, &pasid->src_pasid);
	}

	if (ret < 0)
		dev_err(dev, "no authority:tgid[%u] src_pid[%u] dst_pid[%u]\n",
			pid, task_list.src_process_id, task_list.dst_process_id);

	return ret;
}

static void sdma_fill_sqe(struct hisi_sdma_sq_entry *sq_entry, struct hisi_sdma_sqe_task *task,
			  struct pasid_info pasid, u32 sq_tail, u32 streamid)
{
	sq_entry->opcode          = task->opcode;
	sq_entry->src_streamid    = streamid;
	sq_entry->dst_streamid    = streamid;
	sq_entry->src_addr_l      = (u32)(task->src_addr & 0xffffffff);
	sq_entry->src_addr_h      = (u32)(task->src_addr >> 32);
	sq_entry->dst_addr_l      = (u32)(task->dst_addr & 0xffffffff);
	sq_entry->dst_addr_h      = (u32)(task->dst_addr >> 32);
	sq_entry->length_move     = task->length;
	sq_entry->sns             = 1;
	sq_entry->dns             = 1;
	sq_entry->comp_en         = 1;
	sq_entry->mpamns          = 1;
	sq_entry->sssv            = 1;
	sq_entry->dssv            = 1;
	sq_entry->src_substreamid = pasid.src_pasid;
	sq_entry->dst_substreamid = pasid.dst_pasid;
	sq_entry->sqe_id          = sq_tail;
	sq_entry->src_stride_len  = task->src_stride_len;
	sq_entry->dst_stride_len  = task->dst_stride_len;
	sq_entry->stride_num      = task->stride_num;
	sq_entry->stride          = task->stride_num ? 1 : 0;
	sq_entry->mpam_partid     = task->mpam_partid;
	sq_entry->pmg             = task->pmg;
	sq_entry->qos             = task->qos;
}

static int sdma_send_task_kernel(struct file_open_data *data, struct hisi_sdma_task_info *task_info,
				 struct hisi_sdma_sqe_task *task_list)
{
	struct hisi_sdma_device *pdev = data->psdma_dev;
	struct hisi_sdma_channel *pchannel;
	struct hisi_sdma_sq_entry *sqe;
	struct pasid_info pasid;
	u32 sq_tail;
	int ret;
	u32 i;

	pchannel = pdev->channels + task_info->chn;
	sq_tail = pchannel->sync_info_base->sq_tail;
	for (i = 0; i < task_info->task_cnt; i++) {
		if (task_info->req_cnt != 0) {
			if (task_list[i].length == 0) {
				task_info->req_cnt--;
				continue;
			}
		}
		ret = sdma_verify_src_dst(data, &pasid, task_list[i]);
		if (ret < 0) {
			dev_err(&pdev->pdev->dev, "no correct pid\n");
			return ret;
		}
		sqe = pchannel->sq_base + sq_tail;
		sdma_fill_sqe(sqe, &task_list[i], pasid, sq_tail, pdev->streamid);
		sq_tail = (sq_tail + 1) & (HISI_SDMA_SQ_LENGTH - 1);
	}
	sdma_channel_set_sq_tail(pchannel, sq_tail);
	pchannel->sync_info_base->sq_tail = sq_tail;

	return 0;
}

static int ioctl_sdma_send_task(struct file *file, unsigned long arg)
{
	struct file_open_data *data = file->private_data;
	struct hisi_sdma_task_info task_info;
	struct hisi_sdma_sqe_task *task_list;
	int ret;

	if (copy_from_user(&task_info, (struct hisi_sdma_task_info __user *)(uintptr_t)arg,
			   sizeof(struct hisi_sdma_task_info))) {
		dev_err(&data->psdma_dev->pdev->dev, "get hisi_sdma_task_info failed\n");
		return -EFAULT;
	}
	task_list = kcalloc_node(task_info.task_cnt, sizeof(struct hisi_sdma_sqe_task), GFP_KERNEL,
				 data->psdma_dev->node_idx);
	if (!task_list)
		return -ENOMEM;

	if (copy_from_user(task_list, (void __user *)task_info.task_addr,
			   task_info.task_cnt * sizeof(struct hisi_sdma_sqe_task))) {
		dev_err(&data->psdma_dev->pdev->dev, "get hisi_sdma_sqe_task failed\n");
		ret = -EFAULT;
		goto free_list;
	}
	ret = sdma_send_task_kernel(data, &task_info, task_list);
	if (ret < 0)
		dev_err(&data->psdma_dev->pdev->dev, "exec sdma_send_task_kernel failed\n");

	if (copy_to_user((struct hisi_sdma_task_info __user *)(uintptr_t)arg, &task_info,
			 sizeof(struct hisi_sdma_task_info))) {
		dev_err(&data->psdma_dev->pdev->dev, "set hisi_sdma_task_info failed\n");
		ret = -EFAULT;
	}

free_list:
	kfree(task_list);
	return ret;
}

static int sdma_operation_reg(struct hisi_sdma_device *pdev, unsigned long arg,
			      u32 (*get_func)(struct hisi_sdma_channel *),
			      void (*set_func)(struct hisi_sdma_channel *, u32))
{
	struct device *dev = &pdev->pdev->dev;
	struct hisi_sdma_reg_info reg_info;
	struct hisi_sdma_channel *pchannel;

	if (copy_from_user(&reg_info, (struct hisi_sdma_reg_info __user *)(uintptr_t)arg,
			   sizeof(struct hisi_sdma_reg_info))) {
		dev_err(dev, "get hisi_sdma_reg_info failed\n");
		return -EFAULT;
	}

	pchannel = pdev->channels + reg_info.chn;
	if (reg_info.type == HISI_SDMA_READ_REG) {
		reg_info.reg_value = get_func(pchannel);
		if (copy_to_user((struct hisi_sdma_reg_info __user *)(uintptr_t)arg, &reg_info,
				 sizeof(struct hisi_sdma_reg_info)))
			return -EFAULT;
	} else if (reg_info.type == HISI_SDMA_WRITE_REG) {
		if (!set_func)
			dev_err(dev, "write operation not supported\n");
		else
			set_func(pchannel, reg_info.reg_value);
	}

	return 0;
}

static int ioctl_sdma_sq_head_reg(struct file *file, unsigned long arg)
{
	struct file_open_data *data = file->private_data;
	struct hisi_sdma_device *pdev = data->psdma_dev;

	return sdma_operation_reg(pdev, arg, sdma_channel_get_sq_head, NULL);
}

static int ioctl_sdma_sq_tail_reg(struct file *file, unsigned long arg)
{
	struct file_open_data *data = file->private_data;
	struct hisi_sdma_device *pdev = data->psdma_dev;

	return sdma_operation_reg(pdev, arg, sdma_channel_get_sq_tail, sdma_channel_set_sq_tail);
}

static int ioctl_sdma_cq_head_reg(struct file *file, unsigned long arg)
{
	struct file_open_data *data = file->private_data;
	struct hisi_sdma_device *pdev = data->psdma_dev;

	return sdma_operation_reg(pdev, arg, sdma_channel_get_cq_head, sdma_channel_set_cq_head);
}

static int ioctl_sdma_cq_tail_reg(struct file *file, unsigned long arg)
{
	struct file_open_data *data = file->private_data;
	struct hisi_sdma_device *pdev = data->psdma_dev;

	return sdma_operation_reg(pdev, arg, sdma_channel_get_cq_tail, NULL);
}

static int ioctl_sdma_dfx_reg(struct file *file, unsigned long arg)
{
	struct file_open_data *data = file->private_data;
	struct hisi_sdma_device *pdev = data->psdma_dev;
	struct hisi_sdma_reg_info reg_info;
	struct hisi_sdma_channel *pchannel;
	struct device *dev;

	dev = &pdev->pdev->dev;
	if (copy_from_user(&reg_info, (struct hisi_sdma_reg_info __user *)(uintptr_t)arg,
			   sizeof(struct hisi_sdma_reg_info))) {
		dev_err(dev, "get hisi_sdma_dfx_reg failed\n");
		return -EFAULT;
	}

	pchannel = pdev->channels + reg_info.chn;
	reg_info.reg_value = sdma_channel_get_dfx(pchannel);
	if (copy_to_user((struct hisi_sdma_reg_info __user *)(uintptr_t)arg, &reg_info,
			 sizeof(struct hisi_sdma_reg_info)))
		return -EFAULT;

	return 0;
}

static int ioctl_sdma_sqe_cnt_reg(struct file *file, unsigned long arg)
{
	struct file_open_data *data = file->private_data;
	struct hisi_sdma_device *pdev = data->psdma_dev;
	struct hisi_sdma_reg_info reg_info;
	struct hisi_sdma_channel *pchannel;
	struct device *dev;

	dev = &pdev->pdev->dev;
	if (copy_from_user(&reg_info, (struct hisi_sdma_reg_info __user *)(uintptr_t)arg,
			   sizeof(struct hisi_sdma_reg_info))) {
		dev_err(dev, "get hisi_sdma_reg_info failed\n");
		return -EFAULT;
	}

	pchannel = pdev->channels + reg_info.chn;
	if (reg_info.type == HISI_SDMA_CLR_NORMAL_SQE_CNT)
		sdma_channel_clr_normal_sqe_cnt(pchannel);
	else if (reg_info.type == HISI_SDMA_CLR_ERR_SQE_CNT)
		sdma_channel_clr_err_sqe_cnt(pchannel);

	return 0;
}

struct hisi_sdma_ioctl_func_list g_ioctl_funcs[] = {
	{IOCTL_SDMA_GET_PROCESS_ID,        ioctl_sdma_get_process_id},
	{IOCTL_SDMA_GET_CHN,               ioctl_sdma_get_chn},
	{IOCTL_SDMA_PUT_CHN,               ioctl_sdma_put_chn},
	{IOCTL_SDMA_GET_STREAMID,          ioctl_sdma_get_streamid},
	{IOCTL_GET_SDMA_NUM,               ioctl_get_sdma_num},
	{IOCTL_GET_NEAR_SDMAID,            ioctl_get_near_sdmaid},
	{IOCTL_GET_SDMA_CHN_NUM,           ioctl_get_sdma_chn_num},
	{IOCTL_SDMA_CHN_USED_REFCOUNT,     ioctl_sdma_chn_used_refcount},
	{IOCTL_SDMA_ADD_AUTH_HT,           ioctl_sdma_add_authority_ht},
	{IOCTL_SDMA_SEND_TASK,             ioctl_sdma_send_task},
	{IOCTL_SDMA_SQ_HEAD_REG,           ioctl_sdma_sq_head_reg},
	{IOCTL_SDMA_SQ_TAIL_REG,           ioctl_sdma_sq_tail_reg},
	{IOCTL_SDMA_CQ_HEAD_REG,           ioctl_sdma_cq_head_reg},
	{IOCTL_SDMA_CQ_TAIL_REG,           ioctl_sdma_cq_tail_reg},
	{IOCTL_SDMA_DFX_REG,               ioctl_sdma_dfx_reg},
	{IOCTL_SDMA_SQE_CNT_REG,           ioctl_sdma_sqe_cnt_reg},
};

static long sdma_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
@@ -328,7 +696,7 @@ static int sdma_dev_release(struct inode *inode, struct file *file)

	list_for_each_entry_safe(c, n, &data->share_chn_list, chn_list) {
		dev_dbg(dev, "release share_chn%d\n", c->chn_idx);
		pchannel = pdev->channels  c->chn_idx;
		pchannel = pdev->channels + c->chn_idx;
		pchannel->cnt_used--;
		if (pchannel->sync_info_base->lock != 0 &&
			pchannel->sync_info_base->lock_pid == (u32)current->tgid) {
+54 −5
Original line number Diff line number Diff line
@@ -12,7 +12,7 @@
#include "hisi_sdma.h"
#include "sdma_reg.h"

+#define RW_R_R			0644
#define RW_R_R			0644

#define sdma_wmb() (asm volatile("dsb st" ::: "memory"))

@@ -46,7 +46,7 @@ struct hisi_sdma_channel {
 * @channels: Pointer to the hisi_sdma_channel structure
 * @channel_map: Bitmap indicating the usage of the SDMA channel
 * @io_orig_base: I/O base address after mapping
 * @io_base: io_orig_base  32 channel address offsets
 * @io_base: io_orig_base + 32 channel address offsets
 * @base_addr: SDMA I/O base phyisical address
 * @name: SDMA device name in the /dev directory
 */
@@ -91,18 +91,18 @@ void sdma_info_sync_cdev(struct hisi_sdma_global_info *g_info);

static inline void chn_set_val(struct hisi_sdma_channel *pchan, int reg, u32 val, u32 mask)
{
	u32 reg_val = readl(pchan->io_base  reg);
	u32 reg_val = readl(pchan->io_base + reg);

	reg_val &= ~mask;
	reg_val |= FIELD_PREP(mask, val);
	sdma_wmb();

	writel(reg_val, pchan->io_base  reg);
	writel(reg_val, pchan->io_base + reg);
}

static inline u32 chn_get_val(struct hisi_sdma_channel *pchan, int reg, u32 mask)
{
	u32 reg_val = readl(pchan->io_base  reg);
	u32 reg_val = readl(pchan->io_base + reg);

	return FIELD_GET(mask, reg_val);
}
@@ -194,4 +194,53 @@ static inline u32 sdma_channel_get_cq_head(struct hisi_sdma_channel *pchan)
	return chn_get_val(pchan, HISI_SDMA_CH_CQHDBR_REG, HISI_SDMA_U32_MSK);
}

static inline void sdma_channel_set_irq_mask(void __iomem *io_addr, u32 val)
{
	writel(val, io_addr + HISI_SDMA_CH_IRQ_CTRL_REG);
}

static inline u32 sdma_channel_get_err_status(struct hisi_sdma_channel *pchan)
{
	return chn_get_val(pchan, HISI_SDMA_IRQ_STATUS, HISI_SDMA_CHN_IRQ_STATUS_MSK);
}

static inline void sdma_channel_clear_ioe_status(void __iomem *io_addr)
{
	U_SDMAM_IRQ_STATUS reg_val = {0};

	reg_val.bits.ch_ioe_status = 1;
	writel(HISI_SDMA_U32_MSK, io_addr + HISI_SDMA_IRQ_STATUS);
}

static inline u32 sdma_channel_get_cqe_status(struct hisi_sdma_channel *pchan)
{
	return chn_get_val(pchan, HISI_SDMA_CH_CQE_STATUS_REG, HISI_SDMA_CHN_CQE_STATUS_MSK);
}

static inline u32 sdma_channel_get_cqe_sqeid(struct hisi_sdma_channel *pchan)
{
	return chn_get_val(pchan, HISI_SDMA_CH_CQE_STATUS_REG, HISI_SDMA_CHN_CQE_SQEID_MSK);
}

static inline void sdma_channel_clear_cqe_status(void __iomem *io_addr)
{
	writel(HISI_SDMA_U32_MSK, io_addr + HISI_SDMA_CH_CQE_STATUS_REG);
}

static inline u32 sdma_channel_get_dfx(struct hisi_sdma_channel *pchan)
{
	return chn_get_val(pchan, HISI_SDMA_CH_DFX_REG, HISI_SDMA_U32_MSK);
}

static inline void sdma_channel_clr_normal_sqe_cnt(struct hisi_sdma_channel *pchan)
{
	chn_set_val(pchan, HISI_SDMA_CH_DFX_REG, 0, HISI_SDMA_CHN_NORMAL_SQE_CNT_MSK);
}

static inline void sdma_channel_clr_err_sqe_cnt(struct hisi_sdma_channel *pchan)
{
	chn_set_val(pchan, HISI_SDMA_CH_DFX_REG, 0, HISI_SDMA_CHN_ERROR_SQE_CNT_MSK);
}


#endif
Loading