Commit 60bf9d96 authored by zhangshuowen96's avatar zhangshuowen96
Browse files

drivers: misc: sdma-dae: support initializ sdma driver

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


CVE: NA

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

Add initialization process of sdma driver: sdma-dae.ko,
including necessary steps like: enabling smmu, getting
address resources from bios and so on; add multi-device
management: sdma device use platform device framework
and use char device to communicate with users.

Signed-off-by: default avatarzhangshuowen96 <zhangshuowen@hisilicon.com>
parent 0e64dda2
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __HISI_SDMA_H__
#define __HISI_SDMA_H__

#include <asm-generic/ioctl.h>
#include <linux/errno.h>
#include <linux/types.h>

#define HISI_SDMA_DEVICE_NAME			"sdma"
#define HISI_SDMA_MAX_DEVS			4

#define HISI_STARS_CHN_NUM			32
#define HISI_SDMA_DEFAULT_CHANNEL_NUM		(192 - HISI_STARS_CHN_NUM)
#define HISI_SDMA_REG_SIZE			4096
#define HISI_SDMA_CH_OFFSET			(HISI_STARS_CHN_NUM * HISI_SDMA_REG_SIZE)
#define HISI_SDMA_DEVICE_NAME_MAX		20

#endif
+29 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/sort.h>
#include "sdma_hal.h"

static struct hisi_sdma_global_info g_info;

static const struct file_operations sdma_core_fops = {
	.owner = THIS_MODULE,
	.open = NULL,
	.read = NULL,
	.release = NULL,
	.unlocked_ioctl = NULL,
	.mmap = NULL,
};

void sdma_cdev_init(struct cdev *cdev)
{
	cdev_init(cdev, &sdma_core_fops);
	cdev->owner = THIS_MODULE;
}

void sdma_info_sync_cdev(struct hisi_sdma_global_info *g_info_input)
{
	g_info.core_dev = g_info_input->core_dev;
	g_info.fd_ida = g_info_input->fd_ida;
}
+86 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __HISI_SDMA_HAL_H__
#define __HISI_SDMA_HAL_H__

#include <linux/bitfield.h>
#include <linux/cdev.h>
#include <linux/idr.h>
#include <linux/types.h>
#include <linux/hashtable.h>
#include <linux/io.h>

#include "hisi_sdma.h"

/**
 * struct hisi_sdma_channel - Information about one channel in the SDMA device
 * @idx: SDMA channel's ID
 * @sq_base: Base address of the CQE queue in the DDR
 * @cq_base: Base address of the SQE queue in the DDR
 * @sync_info_base: Share information for user driver in the DDR
 * @io_base: SDMA channel address
 * @cnt_used: Number of times that the channel is used
 */
struct hisi_sdma_channel {
	u16 idx;
	struct hisi_sdma_device *pdev;

	/* must be page-aligned and continuous physical memory */
	struct hisi_sdma_sq_entry *sq_base;
	struct hisi_sdma_cq_entry *cq_base;
	struct hisi_sdma_queue_info *sync_info_base;

	void __iomem *io_base;
	u16 cnt_used;
};


/**
 * struct hisi_sdma_device - SDMA device structure
 * @idx: SDMA device's ID
 * @nr_channel: Number of channels owned by SDMA devices
 * @nr_channel_used: Number of channels used by SDMA devices
 * @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
 * @base_addr: SDMA I/O base phyisical address
 * @name: SDMA device name in the /dev directory
 */

struct hisi_sdma_device {
	u16 idx;
	u16 node_idx;
	u16 nr_channel;
	u16 nr_channel_used;
	spinlock_t channel_lock;
	struct hisi_sdma_channel *channels;
	char name[HISI_SDMA_DEVICE_NAME_MAX];
	DECLARE_BITMAP(channel_map, HISI_SDMA_DEFAULT_CHANNEL_NUM);

	struct platform_device *pdev;
	struct cdev cdev;
	u32 streamid;

	void __iomem *io_orig_base;
	void __iomem *io_base;
	void __iomem *common_base;
	u64 base_addr;
	resource_size_t base_addr_size;
	u64 common_base_addr;
	resource_size_t common_base_addr_size;
};

struct hisi_sdma_core_device {
	u32 sdma_major;
	u32 sdma_device_num;
	struct hisi_sdma_device *sdma_devices[HISI_SDMA_MAX_DEVS];
};

struct hisi_sdma_global_info {
	struct hisi_sdma_core_device *core_dev;
	struct ida *fd_ida;
};

void sdma_cdev_init(struct cdev *cdev);
void sdma_info_sync_cdev(struct hisi_sdma_global_info *g_info);
#endif
+303 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
#include <linux/acpi.h>
#include <linux/bitmap.h>
#include <linux/dma-iommu.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
#include <linux/delay.h>
#include <linux/device.h>

#include "sdma_hal.h"

struct ida fd_ida;
struct hisi_sdma_core_device hisi_sdma_core_device = {0};
static struct class *sdma_class;

static int sdma_device_add(struct hisi_sdma_device *psdma_dev)
{
	u32 idx = psdma_dev->idx;
	struct cdev *cdev = NULL;
	u32 sdma_minor;
	int devno;
	int ret;

	if (idx >= HISI_SDMA_MAX_DEVS) {
		pr_err("Exceeded the maximum number of devices\n");
		goto out_err;
	}

	sdma_minor = idx;
	hisi_sdma_core_device.sdma_devices[idx] = psdma_dev;
	cdev = &hisi_sdma_core_device.sdma_devices[idx]->cdev;
	devno = MKDEV(hisi_sdma_core_device.sdma_major, sdma_minor);

	sdma_cdev_init(cdev);
	ret = cdev_add(cdev, devno, 1);
	if (ret) {
		pr_err("Error %d adding sdma", ret);
		goto out_err;
	}

	if (IS_ERR(device_create(sdma_class, NULL, devno, NULL, "sdma%u", idx))) {
		pr_err("device_create failed\n");
		cdev_del(cdev);
		goto out_err;
	}

	hisi_sdma_core_device.sdma_device_num++;

	return 0;

out_err:
	return -ENODEV;
}

static void sdma_device_delete(struct hisi_sdma_device *psdma_dev)
{
	if (hisi_sdma_core_device.sdma_device_num == 0)
		return;

	hisi_sdma_core_device.sdma_device_num--;
	hisi_sdma_core_device.sdma_devices[psdma_dev->idx] = NULL;
}

static int of_sdma_collect_info(struct platform_device *pdev, struct hisi_sdma_device *psdma_dev)
{
	struct resource *res;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (res == NULL) {
		pr_err("get io_base info from dtb failed\n");
		return -ENOMEM;
	}
	psdma_dev->base_addr = res->start;
	psdma_dev->base_addr_size = resource_size(res);

	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
	if (res == NULL) {
		pr_err("get common reg info from dtb failed\n");
		return -ENOMEM;
	}
	psdma_dev->common_base_addr = res->start;
	psdma_dev->common_base_addr_size = resource_size(res);

	return 0;
}

static int parse_sdma(struct hisi_sdma_device *psdma_dev, struct platform_device *pdev)
{
	u32 nr_channel;

	if (device_property_read_u32(&pdev->dev, "sdma-chn-num", &nr_channel)) {
		pr_err("ACPI sdma-chn-num get failed!\n");
		psdma_dev->nr_channel = HISI_SDMA_DEFAULT_CHANNEL_NUM;
	} else {
		if (nr_channel <= HISI_STARS_CHN_NUM) {
			pr_err("ACPI sdma-chn-num = %u is insufficient\n", nr_channel);
			return -1;
		}
		psdma_dev->nr_channel = (u16)(nr_channel - HISI_STARS_CHN_NUM);
	}

	return 0;
}

static int sdma_init_device_info(struct hisi_sdma_device *psdma_dev)
{
	int ret;

	psdma_dev->io_orig_base = ioremap(psdma_dev->base_addr, psdma_dev->base_addr_size);
	if (!psdma_dev->io_orig_base)
		return -ENOMEM;

	psdma_dev->common_base = ioremap(psdma_dev->common_base_addr,
					 psdma_dev->common_base_addr_size);
	if (!psdma_dev->common_base) {
		iounmap(psdma_dev->io_orig_base);
		return -ENOMEM;
	}
	psdma_dev->io_base = psdma_dev->io_orig_base + HISI_SDMA_CH_OFFSET;

	return 0;
}

static int sdma_smmu_enable(struct device *dev)
{
	int ret;

	ret = iommu_dev_enable_feature(dev, IOMMU_DEV_FEAT_IOPF);
	if (ret) {
		pr_err("failed to enable IOPF feature! ret = %p\n", ERR_PTR(ret));
		return ret;
	}

	ret = iommu_dev_enable_feature(dev, IOMMU_DEV_FEAT_SVA);
	if (ret) {
		pr_err("failed to enable SVA feature! ret = %p\n", ERR_PTR(ret));
		iommu_dev_disable_feature(dev, IOMMU_DEV_FEAT_IOPF);
		return ret;
	}

	return 0;
}

static int sdma_device_probe(struct platform_device *pdev)
{
	struct hisi_sdma_device *psdma_dev;
	u32 device_num;
	int ret;

	device_num = hisi_sdma_core_device.sdma_device_num;
	psdma_dev = kzalloc_node(sizeof(*psdma_dev), GFP_KERNEL, pdev->dev.numa_node);
	if (!psdma_dev)
		return -ENOMEM;

	psdma_dev->idx = device_num;
	psdma_dev->node_idx = pdev->dev.numa_node;
	ret = parse_sdma(psdma_dev, pdev);
	if (ret < 0)
		goto free_dev;

	psdma_dev->pdev = pdev;
	dev_set_drvdata(&pdev->dev, psdma_dev);

	ret = of_sdma_collect_info(pdev, psdma_dev);
	if (ret < 0) {
		pr_err("collect device info failed, %d\n", ret);
		goto free_dev;
	}

	ret = sdma_init_device_info(psdma_dev);
	if (ret < 0)
		goto free_dev;

	ret = sdma_smmu_enable(&pdev->dev);
	if (ret)
		goto deinit_device;

	psdma_dev->streamid = pdev->dev.iommu->fwspec->ids[0];
	spin_lock_init(&psdma_dev->channel_lock);

	ret = sdma_device_add(psdma_dev);
	if (ret)
		goto sva_device_shutdown;

	dev_info(&pdev->dev, "sdma%d registered\n", psdma_dev->idx);

	return 0;

sva_device_shutdown:
	iommu_dev_disable_feature(&pdev->dev, IOMMU_DEV_FEAT_SVA);
	iommu_dev_disable_feature(&pdev->dev, IOMMU_DEV_FEAT_IOPF);
deinit_device:
	iounmap(psdma_dev->common_base);
	iounmap(psdma_dev->io_orig_base);
free_dev:
	kfree(psdma_dev);

	return ret;
}

static int sdma_device_remove(struct platform_device *pdev)
{
	struct hisi_sdma_device *psdma_dev = dev_get_drvdata(&pdev->dev);

	sdma_device_delete(psdma_dev);
	device_destroy(sdma_class, MKDEV(hisi_sdma_core_device.sdma_major, psdma_dev->idx));
	cdev_del(&psdma_dev->cdev);

	iommu_dev_disable_feature(&pdev->dev, IOMMU_DEV_FEAT_SVA);
	iommu_dev_disable_feature(&pdev->dev, IOMMU_DEV_FEAT_IOPF);
	iounmap(psdma_dev->io_orig_base);
	iounmap(psdma_dev->common_base);

	kfree(psdma_dev);

	return 0;
}

static const struct acpi_device_id sdma_acpi_match[] = {
	{ "HISI0431", 0 },
	{}
};
MODULE_DEVICE_TABLE(acpi, sdma_acpi_match);

static struct platform_driver sdma_driver = {
	.probe    = sdma_device_probe,
	.remove   = sdma_device_remove,
	.driver   = {
		.name             = HISI_SDMA_DEVICE_NAME,
		.acpi_match_table = sdma_acpi_match,
	},
};

static void global_var_init(struct hisi_sdma_global_info *g_info)
{
	ida_init(&fd_ida);

	g_info->core_dev = &hisi_sdma_core_device;
	g_info->fd_ida = &fd_ida;
}

static int __init sdma_driver_init(void)
{
	struct hisi_sdma_global_info *g_info = NULL;
	dev_t sdma_dev;
	int ret;

	g_info = kcalloc(1, sizeof(struct hisi_sdma_global_info), GFP_KERNEL);
	if (!g_info)
		return -ENOMEM;

	global_var_init(g_info);
	sdma_info_sync_cdev(g_info);

	sdma_class = class_create(THIS_MODULE, "sdma");
	if (IS_ERR(sdma_class)) {
		pr_err("class_create() failed for sdma_class: %d\n", PTR_ERR(sdma_class));
		goto destroy_ida;
	}
	ret = alloc_chrdev_region(&sdma_dev, 0, HISI_SDMA_MAX_DEVS, "sdma");
	if (ret < 0) {
		pr_err("alloc_chrdev_region() failed for sdma\n");
		goto destroy_class;
	}
	hisi_sdma_core_device.sdma_major = MAJOR(sdma_dev);
	ret = platform_driver_register(&sdma_driver);
	if (ret)
		goto unregister_chrdev;

	kfree(g_info);

	return 0;

unregister_chrdev:
	unregister_chrdev_region(sdma_dev, HISI_SDMA_MAX_DEVS);
destroy_class:
	class_destroy(sdma_class);
destroy_ida:
	ida_destroy(&fd_ida);
	kfree(g_info);

	return -ENODEV;
}

static void __exit sdma_driver_exit(void)
{
	dev_t devno;

	platform_driver_unregister(&sdma_driver);

	devno = MKDEV(hisi_sdma_core_device.sdma_major, 0);
	unregister_chrdev_region(devno, HISI_SDMA_MAX_DEVS);
	class_destroy(sdma_class);
	ida_destroy(&fd_ida);
}

module_init(sdma_driver_init);
module_exit(sdma_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("HiSilicon Tech. Co., Ltd.");
MODULE_DESCRIPTION("SDMA data accelerator engine for Userland applications");
+5 −0
Original line number Diff line number Diff line
@@ -263,6 +263,11 @@ F: drivers/iommu/hisilicon
F:	drivers/ubc
F:	drivers/vfio/ubc

SDMA DRIVER
M:	Xiaoxu Zeng <zengxiaoxu@huawei.com>
S:	Maintained
F:	drivers/misc/sdma-dae

VDPA DRIVER
M:	jiangdongxu <jiangdongxu1@huawei.com>
S:	Maintained