18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Qualcomm Technologies HIDMA Management SYS interface
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2015, The Linux Foundation. All rights reserved.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/sysfs.h>
98c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "hidma_mgmt.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistruct hidma_chan_attr {
148c2ecf20Sopenharmony_ci	struct hidma_mgmt_dev *mdev;
158c2ecf20Sopenharmony_ci	int index;
168c2ecf20Sopenharmony_ci	struct kobj_attribute attr;
178c2ecf20Sopenharmony_ci};
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistruct hidma_mgmt_fileinfo {
208c2ecf20Sopenharmony_ci	char *name;
218c2ecf20Sopenharmony_ci	int mode;
228c2ecf20Sopenharmony_ci	int (*get)(struct hidma_mgmt_dev *mdev);
238c2ecf20Sopenharmony_ci	int (*set)(struct hidma_mgmt_dev *mdev, u64 val);
248c2ecf20Sopenharmony_ci};
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define IMPLEMENT_GETSET(name)					\
278c2ecf20Sopenharmony_cistatic int get_##name(struct hidma_mgmt_dev *mdev)		\
288c2ecf20Sopenharmony_ci{								\
298c2ecf20Sopenharmony_ci	return mdev->name;					\
308c2ecf20Sopenharmony_ci}								\
318c2ecf20Sopenharmony_cistatic int set_##name(struct hidma_mgmt_dev *mdev, u64 val)	\
328c2ecf20Sopenharmony_ci{								\
338c2ecf20Sopenharmony_ci	u64 tmp;						\
348c2ecf20Sopenharmony_ci	int rc;							\
358c2ecf20Sopenharmony_ci								\
368c2ecf20Sopenharmony_ci	tmp = mdev->name;					\
378c2ecf20Sopenharmony_ci	mdev->name = val;					\
388c2ecf20Sopenharmony_ci	rc = hidma_mgmt_setup(mdev);				\
398c2ecf20Sopenharmony_ci	if (rc)							\
408c2ecf20Sopenharmony_ci		mdev->name = tmp;				\
418c2ecf20Sopenharmony_ci	return rc;						\
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define DECLARE_ATTRIBUTE(name, mode)				\
458c2ecf20Sopenharmony_ci	{#name, mode, get_##name, set_##name}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ciIMPLEMENT_GETSET(hw_version_major)
488c2ecf20Sopenharmony_ciIMPLEMENT_GETSET(hw_version_minor)
498c2ecf20Sopenharmony_ciIMPLEMENT_GETSET(max_wr_xactions)
508c2ecf20Sopenharmony_ciIMPLEMENT_GETSET(max_rd_xactions)
518c2ecf20Sopenharmony_ciIMPLEMENT_GETSET(max_write_request)
528c2ecf20Sopenharmony_ciIMPLEMENT_GETSET(max_read_request)
538c2ecf20Sopenharmony_ciIMPLEMENT_GETSET(dma_channels)
548c2ecf20Sopenharmony_ciIMPLEMENT_GETSET(chreset_timeout_cycles)
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic int set_priority(struct hidma_mgmt_dev *mdev, unsigned int i, u64 val)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	u64 tmp;
598c2ecf20Sopenharmony_ci	int rc;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	if (i >= mdev->dma_channels)
628c2ecf20Sopenharmony_ci		return -EINVAL;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	tmp = mdev->priority[i];
658c2ecf20Sopenharmony_ci	mdev->priority[i] = val;
668c2ecf20Sopenharmony_ci	rc = hidma_mgmt_setup(mdev);
678c2ecf20Sopenharmony_ci	if (rc)
688c2ecf20Sopenharmony_ci		mdev->priority[i] = tmp;
698c2ecf20Sopenharmony_ci	return rc;
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic int set_weight(struct hidma_mgmt_dev *mdev, unsigned int i, u64 val)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	u64 tmp;
758c2ecf20Sopenharmony_ci	int rc;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (i >= mdev->dma_channels)
788c2ecf20Sopenharmony_ci		return -EINVAL;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	tmp = mdev->weight[i];
818c2ecf20Sopenharmony_ci	mdev->weight[i] = val;
828c2ecf20Sopenharmony_ci	rc = hidma_mgmt_setup(mdev);
838c2ecf20Sopenharmony_ci	if (rc)
848c2ecf20Sopenharmony_ci		mdev->weight[i] = tmp;
858c2ecf20Sopenharmony_ci	return rc;
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic struct hidma_mgmt_fileinfo hidma_mgmt_files[] = {
898c2ecf20Sopenharmony_ci	DECLARE_ATTRIBUTE(hw_version_major, S_IRUGO),
908c2ecf20Sopenharmony_ci	DECLARE_ATTRIBUTE(hw_version_minor, S_IRUGO),
918c2ecf20Sopenharmony_ci	DECLARE_ATTRIBUTE(dma_channels, S_IRUGO),
928c2ecf20Sopenharmony_ci	DECLARE_ATTRIBUTE(chreset_timeout_cycles, S_IRUGO),
938c2ecf20Sopenharmony_ci	DECLARE_ATTRIBUTE(max_wr_xactions, S_IRUGO),
948c2ecf20Sopenharmony_ci	DECLARE_ATTRIBUTE(max_rd_xactions, S_IRUGO),
958c2ecf20Sopenharmony_ci	DECLARE_ATTRIBUTE(max_write_request, S_IRUGO),
968c2ecf20Sopenharmony_ci	DECLARE_ATTRIBUTE(max_read_request, S_IRUGO),
978c2ecf20Sopenharmony_ci};
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic ssize_t show_values(struct device *dev, struct device_attribute *attr,
1008c2ecf20Sopenharmony_ci			   char *buf)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	struct hidma_mgmt_dev *mdev = dev_get_drvdata(dev);
1038c2ecf20Sopenharmony_ci	unsigned int i;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	buf[0] = 0;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {
1088c2ecf20Sopenharmony_ci		if (strcmp(attr->attr.name, hidma_mgmt_files[i].name) == 0) {
1098c2ecf20Sopenharmony_ci			sprintf(buf, "%d\n", hidma_mgmt_files[i].get(mdev));
1108c2ecf20Sopenharmony_ci			break;
1118c2ecf20Sopenharmony_ci		}
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci	return strlen(buf);
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic ssize_t set_values(struct device *dev, struct device_attribute *attr,
1178c2ecf20Sopenharmony_ci			  const char *buf, size_t count)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	struct hidma_mgmt_dev *mdev = dev_get_drvdata(dev);
1208c2ecf20Sopenharmony_ci	unsigned long tmp;
1218c2ecf20Sopenharmony_ci	unsigned int i;
1228c2ecf20Sopenharmony_ci	int rc;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	rc = kstrtoul(buf, 0, &tmp);
1258c2ecf20Sopenharmony_ci	if (rc)
1268c2ecf20Sopenharmony_ci		return rc;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {
1298c2ecf20Sopenharmony_ci		if (strcmp(attr->attr.name, hidma_mgmt_files[i].name) == 0) {
1308c2ecf20Sopenharmony_ci			rc = hidma_mgmt_files[i].set(mdev, tmp);
1318c2ecf20Sopenharmony_ci			if (rc)
1328c2ecf20Sopenharmony_ci				return rc;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci			break;
1358c2ecf20Sopenharmony_ci		}
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci	return count;
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic ssize_t show_values_channel(struct kobject *kobj,
1418c2ecf20Sopenharmony_ci				   struct kobj_attribute *attr, char *buf)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	struct hidma_chan_attr *chattr;
1448c2ecf20Sopenharmony_ci	struct hidma_mgmt_dev *mdev;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	buf[0] = 0;
1478c2ecf20Sopenharmony_ci	chattr = container_of(attr, struct hidma_chan_attr, attr);
1488c2ecf20Sopenharmony_ci	mdev = chattr->mdev;
1498c2ecf20Sopenharmony_ci	if (strcmp(attr->attr.name, "priority") == 0)
1508c2ecf20Sopenharmony_ci		sprintf(buf, "%d\n", mdev->priority[chattr->index]);
1518c2ecf20Sopenharmony_ci	else if (strcmp(attr->attr.name, "weight") == 0)
1528c2ecf20Sopenharmony_ci		sprintf(buf, "%d\n", mdev->weight[chattr->index]);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	return strlen(buf);
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic ssize_t set_values_channel(struct kobject *kobj,
1588c2ecf20Sopenharmony_ci				  struct kobj_attribute *attr, const char *buf,
1598c2ecf20Sopenharmony_ci				  size_t count)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	struct hidma_chan_attr *chattr;
1628c2ecf20Sopenharmony_ci	struct hidma_mgmt_dev *mdev;
1638c2ecf20Sopenharmony_ci	unsigned long tmp;
1648c2ecf20Sopenharmony_ci	int rc;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	chattr = container_of(attr, struct hidma_chan_attr, attr);
1678c2ecf20Sopenharmony_ci	mdev = chattr->mdev;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	rc = kstrtoul(buf, 0, &tmp);
1708c2ecf20Sopenharmony_ci	if (rc)
1718c2ecf20Sopenharmony_ci		return rc;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	if (strcmp(attr->attr.name, "priority") == 0) {
1748c2ecf20Sopenharmony_ci		rc = set_priority(mdev, chattr->index, tmp);
1758c2ecf20Sopenharmony_ci		if (rc)
1768c2ecf20Sopenharmony_ci			return rc;
1778c2ecf20Sopenharmony_ci	} else if (strcmp(attr->attr.name, "weight") == 0) {
1788c2ecf20Sopenharmony_ci		rc = set_weight(mdev, chattr->index, tmp);
1798c2ecf20Sopenharmony_ci		if (rc)
1808c2ecf20Sopenharmony_ci			return rc;
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci	return count;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic int create_sysfs_entry(struct hidma_mgmt_dev *dev, char *name, int mode)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	struct device_attribute *attrs;
1888c2ecf20Sopenharmony_ci	char *name_copy;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	attrs = devm_kmalloc(&dev->pdev->dev,
1918c2ecf20Sopenharmony_ci			     sizeof(struct device_attribute), GFP_KERNEL);
1928c2ecf20Sopenharmony_ci	if (!attrs)
1938c2ecf20Sopenharmony_ci		return -ENOMEM;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	name_copy = devm_kstrdup(&dev->pdev->dev, name, GFP_KERNEL);
1968c2ecf20Sopenharmony_ci	if (!name_copy)
1978c2ecf20Sopenharmony_ci		return -ENOMEM;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	attrs->attr.name = name_copy;
2008c2ecf20Sopenharmony_ci	attrs->attr.mode = mode;
2018c2ecf20Sopenharmony_ci	attrs->show = show_values;
2028c2ecf20Sopenharmony_ci	attrs->store = set_values;
2038c2ecf20Sopenharmony_ci	sysfs_attr_init(&attrs->attr);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	return device_create_file(&dev->pdev->dev, attrs);
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic int create_sysfs_entry_channel(struct hidma_mgmt_dev *mdev, char *name,
2098c2ecf20Sopenharmony_ci				      int mode, int index,
2108c2ecf20Sopenharmony_ci				      struct kobject *parent)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	struct hidma_chan_attr *chattr;
2138c2ecf20Sopenharmony_ci	char *name_copy;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	chattr = devm_kmalloc(&mdev->pdev->dev, sizeof(*chattr), GFP_KERNEL);
2168c2ecf20Sopenharmony_ci	if (!chattr)
2178c2ecf20Sopenharmony_ci		return -ENOMEM;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	name_copy = devm_kstrdup(&mdev->pdev->dev, name, GFP_KERNEL);
2208c2ecf20Sopenharmony_ci	if (!name_copy)
2218c2ecf20Sopenharmony_ci		return -ENOMEM;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	chattr->mdev = mdev;
2248c2ecf20Sopenharmony_ci	chattr->index = index;
2258c2ecf20Sopenharmony_ci	chattr->attr.attr.name = name_copy;
2268c2ecf20Sopenharmony_ci	chattr->attr.attr.mode = mode;
2278c2ecf20Sopenharmony_ci	chattr->attr.show = show_values_channel;
2288c2ecf20Sopenharmony_ci	chattr->attr.store = set_values_channel;
2298c2ecf20Sopenharmony_ci	sysfs_attr_init(&chattr->attr.attr);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	return sysfs_create_file(parent, &chattr->attr.attr);
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ciint hidma_mgmt_init_sys(struct hidma_mgmt_dev *mdev)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	unsigned int i;
2378c2ecf20Sopenharmony_ci	int rc;
2388c2ecf20Sopenharmony_ci	int required;
2398c2ecf20Sopenharmony_ci	struct kobject *chanops;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	required = sizeof(*mdev->chroots) * mdev->dma_channels;
2428c2ecf20Sopenharmony_ci	mdev->chroots = devm_kmalloc(&mdev->pdev->dev, required, GFP_KERNEL);
2438c2ecf20Sopenharmony_ci	if (!mdev->chroots)
2448c2ecf20Sopenharmony_ci		return -ENOMEM;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	chanops = kobject_create_and_add("chanops", &mdev->pdev->dev.kobj);
2478c2ecf20Sopenharmony_ci	if (!chanops)
2488c2ecf20Sopenharmony_ci		return -ENOMEM;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	/* create each channel directory here */
2518c2ecf20Sopenharmony_ci	for (i = 0; i < mdev->dma_channels; i++) {
2528c2ecf20Sopenharmony_ci		char name[20];
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci		snprintf(name, sizeof(name), "chan%d", i);
2558c2ecf20Sopenharmony_ci		mdev->chroots[i] = kobject_create_and_add(name, chanops);
2568c2ecf20Sopenharmony_ci		if (!mdev->chroots[i])
2578c2ecf20Sopenharmony_ci			return -ENOMEM;
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	/* populate common parameters */
2618c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {
2628c2ecf20Sopenharmony_ci		rc = create_sysfs_entry(mdev, hidma_mgmt_files[i].name,
2638c2ecf20Sopenharmony_ci					hidma_mgmt_files[i].mode);
2648c2ecf20Sopenharmony_ci		if (rc)
2658c2ecf20Sopenharmony_ci			return rc;
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	/* populate parameters that are per channel */
2698c2ecf20Sopenharmony_ci	for (i = 0; i < mdev->dma_channels; i++) {
2708c2ecf20Sopenharmony_ci		rc = create_sysfs_entry_channel(mdev, "priority",
2718c2ecf20Sopenharmony_ci						(S_IRUGO | S_IWUGO), i,
2728c2ecf20Sopenharmony_ci						mdev->chroots[i]);
2738c2ecf20Sopenharmony_ci		if (rc)
2748c2ecf20Sopenharmony_ci			return rc;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci		rc = create_sysfs_entry_channel(mdev, "weight",
2778c2ecf20Sopenharmony_ci						(S_IRUGO | S_IWUGO), i,
2788c2ecf20Sopenharmony_ci						mdev->chroots[i]);
2798c2ecf20Sopenharmony_ci		if (rc)
2808c2ecf20Sopenharmony_ci			return rc;
2818c2ecf20Sopenharmony_ci	}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	return 0;
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(hidma_mgmt_init_sys);
286