197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27f8f209fSSinan Kaya /*
37f8f209fSSinan Kaya  * Qualcomm Technologies HIDMA Management SYS interface
47f8f209fSSinan Kaya  *
57f8f209fSSinan Kaya  * Copyright (c) 2015, The Linux Foundation. All rights reserved.
67f8f209fSSinan Kaya  */
77f8f209fSSinan Kaya 
87f8f209fSSinan Kaya #include <linux/sysfs.h>
97f8f209fSSinan Kaya #include <linux/platform_device.h>
107f8f209fSSinan Kaya 
117f8f209fSSinan Kaya #include "hidma_mgmt.h"
127f8f209fSSinan Kaya 
137f8f209fSSinan Kaya struct hidma_chan_attr {
147f8f209fSSinan Kaya 	struct hidma_mgmt_dev *mdev;
157f8f209fSSinan Kaya 	int index;
167f8f209fSSinan Kaya 	struct kobj_attribute attr;
177f8f209fSSinan Kaya };
187f8f209fSSinan Kaya 
197f8f209fSSinan Kaya struct hidma_mgmt_fileinfo {
207f8f209fSSinan Kaya 	char *name;
217f8f209fSSinan Kaya 	int mode;
227f8f209fSSinan Kaya 	int (*get)(struct hidma_mgmt_dev *mdev);
237f8f209fSSinan Kaya 	int (*set)(struct hidma_mgmt_dev *mdev, u64 val);
247f8f209fSSinan Kaya };
257f8f209fSSinan Kaya 
267f8f209fSSinan Kaya #define IMPLEMENT_GETSET(name)					\
277f8f209fSSinan Kaya static int get_##name(struct hidma_mgmt_dev *mdev)		\
287f8f209fSSinan Kaya {								\
297f8f209fSSinan Kaya 	return mdev->name;					\
307f8f209fSSinan Kaya }								\
317f8f209fSSinan Kaya static int set_##name(struct hidma_mgmt_dev *mdev, u64 val)	\
327f8f209fSSinan Kaya {								\
337f8f209fSSinan Kaya 	u64 tmp;						\
347f8f209fSSinan Kaya 	int rc;							\
357f8f209fSSinan Kaya 								\
367f8f209fSSinan Kaya 	tmp = mdev->name;					\
377f8f209fSSinan Kaya 	mdev->name = val;					\
387f8f209fSSinan Kaya 	rc = hidma_mgmt_setup(mdev);				\
397f8f209fSSinan Kaya 	if (rc)							\
407f8f209fSSinan Kaya 		mdev->name = tmp;				\
417f8f209fSSinan Kaya 	return rc;						\
427f8f209fSSinan Kaya }
437f8f209fSSinan Kaya 
447f8f209fSSinan Kaya #define DECLARE_ATTRIBUTE(name, mode)				\
457f8f209fSSinan Kaya 	{#name, mode, get_##name, set_##name}
467f8f209fSSinan Kaya 
477f8f209fSSinan Kaya IMPLEMENT_GETSET(hw_version_major)
IMPLEMENT_GETSET(hw_version_minor)487f8f209fSSinan Kaya IMPLEMENT_GETSET(hw_version_minor)
497f8f209fSSinan Kaya IMPLEMENT_GETSET(max_wr_xactions)
507f8f209fSSinan Kaya IMPLEMENT_GETSET(max_rd_xactions)
517f8f209fSSinan Kaya IMPLEMENT_GETSET(max_write_request)
527f8f209fSSinan Kaya IMPLEMENT_GETSET(max_read_request)
537f8f209fSSinan Kaya IMPLEMENT_GETSET(dma_channels)
547f8f209fSSinan Kaya IMPLEMENT_GETSET(chreset_timeout_cycles)
557f8f209fSSinan Kaya 
567f8f209fSSinan Kaya static int set_priority(struct hidma_mgmt_dev *mdev, unsigned int i, u64 val)
577f8f209fSSinan Kaya {
587f8f209fSSinan Kaya 	u64 tmp;
597f8f209fSSinan Kaya 	int rc;
607f8f209fSSinan Kaya 
617f8f209fSSinan Kaya 	if (i >= mdev->dma_channels)
627f8f209fSSinan Kaya 		return -EINVAL;
637f8f209fSSinan Kaya 
647f8f209fSSinan Kaya 	tmp = mdev->priority[i];
657f8f209fSSinan Kaya 	mdev->priority[i] = val;
667f8f209fSSinan Kaya 	rc = hidma_mgmt_setup(mdev);
677f8f209fSSinan Kaya 	if (rc)
687f8f209fSSinan Kaya 		mdev->priority[i] = tmp;
697f8f209fSSinan Kaya 	return rc;
707f8f209fSSinan Kaya }
717f8f209fSSinan Kaya 
set_weight(struct hidma_mgmt_dev * mdev,unsigned int i,u64 val)727f8f209fSSinan Kaya static int set_weight(struct hidma_mgmt_dev *mdev, unsigned int i, u64 val)
737f8f209fSSinan Kaya {
747f8f209fSSinan Kaya 	u64 tmp;
757f8f209fSSinan Kaya 	int rc;
767f8f209fSSinan Kaya 
777f8f209fSSinan Kaya 	if (i >= mdev->dma_channels)
787f8f209fSSinan Kaya 		return -EINVAL;
797f8f209fSSinan Kaya 
807f8f209fSSinan Kaya 	tmp = mdev->weight[i];
817f8f209fSSinan Kaya 	mdev->weight[i] = val;
827f8f209fSSinan Kaya 	rc = hidma_mgmt_setup(mdev);
837f8f209fSSinan Kaya 	if (rc)
847f8f209fSSinan Kaya 		mdev->weight[i] = tmp;
857f8f209fSSinan Kaya 	return rc;
867f8f209fSSinan Kaya }
877f8f209fSSinan Kaya 
887f8f209fSSinan Kaya static struct hidma_mgmt_fileinfo hidma_mgmt_files[] = {
897f8f209fSSinan Kaya 	DECLARE_ATTRIBUTE(hw_version_major, S_IRUGO),
907f8f209fSSinan Kaya 	DECLARE_ATTRIBUTE(hw_version_minor, S_IRUGO),
917f8f209fSSinan Kaya 	DECLARE_ATTRIBUTE(dma_channels, S_IRUGO),
927f8f209fSSinan Kaya 	DECLARE_ATTRIBUTE(chreset_timeout_cycles, S_IRUGO),
937f8f209fSSinan Kaya 	DECLARE_ATTRIBUTE(max_wr_xactions, S_IRUGO),
947f8f209fSSinan Kaya 	DECLARE_ATTRIBUTE(max_rd_xactions, S_IRUGO),
957f8f209fSSinan Kaya 	DECLARE_ATTRIBUTE(max_write_request, S_IRUGO),
967f8f209fSSinan Kaya 	DECLARE_ATTRIBUTE(max_read_request, S_IRUGO),
977f8f209fSSinan Kaya };
987f8f209fSSinan Kaya 
show_values(struct device * dev,struct device_attribute * attr,char * buf)997f8f209fSSinan Kaya static ssize_t show_values(struct device *dev, struct device_attribute *attr,
1007f8f209fSSinan Kaya 			   char *buf)
1017f8f209fSSinan Kaya {
1026af6c371SWolfram Sang 	struct hidma_mgmt_dev *mdev = dev_get_drvdata(dev);
1037f8f209fSSinan Kaya 	unsigned int i;
1047f8f209fSSinan Kaya 
1057f8f209fSSinan Kaya 	buf[0] = 0;
1067f8f209fSSinan Kaya 
1077f8f209fSSinan Kaya 	for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {
1087f8f209fSSinan Kaya 		if (strcmp(attr->attr.name, hidma_mgmt_files[i].name) == 0) {
1097f8f209fSSinan Kaya 			sprintf(buf, "%d\n", hidma_mgmt_files[i].get(mdev));
1107f8f209fSSinan Kaya 			break;
1117f8f209fSSinan Kaya 		}
1127f8f209fSSinan Kaya 	}
1137f8f209fSSinan Kaya 	return strlen(buf);
1147f8f209fSSinan Kaya }
1157f8f209fSSinan Kaya 
set_values(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1167f8f209fSSinan Kaya static ssize_t set_values(struct device *dev, struct device_attribute *attr,
1177f8f209fSSinan Kaya 			  const char *buf, size_t count)
1187f8f209fSSinan Kaya {
1196af6c371SWolfram Sang 	struct hidma_mgmt_dev *mdev = dev_get_drvdata(dev);
1207f8f209fSSinan Kaya 	unsigned long tmp;
1217f8f209fSSinan Kaya 	unsigned int i;
1227f8f209fSSinan Kaya 	int rc;
1237f8f209fSSinan Kaya 
1247f8f209fSSinan Kaya 	rc = kstrtoul(buf, 0, &tmp);
1257f8f209fSSinan Kaya 	if (rc)
1267f8f209fSSinan Kaya 		return rc;
1277f8f209fSSinan Kaya 
1287f8f209fSSinan Kaya 	for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {
1297f8f209fSSinan Kaya 		if (strcmp(attr->attr.name, hidma_mgmt_files[i].name) == 0) {
1307f8f209fSSinan Kaya 			rc = hidma_mgmt_files[i].set(mdev, tmp);
1317f8f209fSSinan Kaya 			if (rc)
1327f8f209fSSinan Kaya 				return rc;
1337f8f209fSSinan Kaya 
1347f8f209fSSinan Kaya 			break;
1357f8f209fSSinan Kaya 		}
1367f8f209fSSinan Kaya 	}
1377f8f209fSSinan Kaya 	return count;
1387f8f209fSSinan Kaya }
1397f8f209fSSinan Kaya 
show_values_channel(struct kobject * kobj,struct kobj_attribute * attr,char * buf)1407f8f209fSSinan Kaya static ssize_t show_values_channel(struct kobject *kobj,
1417f8f209fSSinan Kaya 				   struct kobj_attribute *attr, char *buf)
1427f8f209fSSinan Kaya {
1437f8f209fSSinan Kaya 	struct hidma_chan_attr *chattr;
1447f8f209fSSinan Kaya 	struct hidma_mgmt_dev *mdev;
1457f8f209fSSinan Kaya 
1467f8f209fSSinan Kaya 	buf[0] = 0;
1477f8f209fSSinan Kaya 	chattr = container_of(attr, struct hidma_chan_attr, attr);
1487f8f209fSSinan Kaya 	mdev = chattr->mdev;
1497f8f209fSSinan Kaya 	if (strcmp(attr->attr.name, "priority") == 0)
1507f8f209fSSinan Kaya 		sprintf(buf, "%d\n", mdev->priority[chattr->index]);
1517f8f209fSSinan Kaya 	else if (strcmp(attr->attr.name, "weight") == 0)
1527f8f209fSSinan Kaya 		sprintf(buf, "%d\n", mdev->weight[chattr->index]);
1537f8f209fSSinan Kaya 
1547f8f209fSSinan Kaya 	return strlen(buf);
1557f8f209fSSinan Kaya }
1567f8f209fSSinan Kaya 
set_values_channel(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)1577f8f209fSSinan Kaya static ssize_t set_values_channel(struct kobject *kobj,
1587f8f209fSSinan Kaya 				  struct kobj_attribute *attr, const char *buf,
1597f8f209fSSinan Kaya 				  size_t count)
1607f8f209fSSinan Kaya {
1617f8f209fSSinan Kaya 	struct hidma_chan_attr *chattr;
1627f8f209fSSinan Kaya 	struct hidma_mgmt_dev *mdev;
1637f8f209fSSinan Kaya 	unsigned long tmp;
1647f8f209fSSinan Kaya 	int rc;
1657f8f209fSSinan Kaya 
1667f8f209fSSinan Kaya 	chattr = container_of(attr, struct hidma_chan_attr, attr);
1677f8f209fSSinan Kaya 	mdev = chattr->mdev;
1687f8f209fSSinan Kaya 
1697f8f209fSSinan Kaya 	rc = kstrtoul(buf, 0, &tmp);
1707f8f209fSSinan Kaya 	if (rc)
1717f8f209fSSinan Kaya 		return rc;
1727f8f209fSSinan Kaya 
1737f8f209fSSinan Kaya 	if (strcmp(attr->attr.name, "priority") == 0) {
1747f8f209fSSinan Kaya 		rc = set_priority(mdev, chattr->index, tmp);
1757f8f209fSSinan Kaya 		if (rc)
1767f8f209fSSinan Kaya 			return rc;
1777f8f209fSSinan Kaya 	} else if (strcmp(attr->attr.name, "weight") == 0) {
1787f8f209fSSinan Kaya 		rc = set_weight(mdev, chattr->index, tmp);
1797f8f209fSSinan Kaya 		if (rc)
1807f8f209fSSinan Kaya 			return rc;
1817f8f209fSSinan Kaya 	}
1827f8f209fSSinan Kaya 	return count;
1837f8f209fSSinan Kaya }
1847f8f209fSSinan Kaya 
create_sysfs_entry(struct hidma_mgmt_dev * dev,char * name,int mode)1857f8f209fSSinan Kaya static int create_sysfs_entry(struct hidma_mgmt_dev *dev, char *name, int mode)
1867f8f209fSSinan Kaya {
1877f8f209fSSinan Kaya 	struct device_attribute *attrs;
1887f8f209fSSinan Kaya 	char *name_copy;
1897f8f209fSSinan Kaya 
1907f8f209fSSinan Kaya 	attrs = devm_kmalloc(&dev->pdev->dev,
1917f8f209fSSinan Kaya 			     sizeof(struct device_attribute), GFP_KERNEL);
1927f8f209fSSinan Kaya 	if (!attrs)
1937f8f209fSSinan Kaya 		return -ENOMEM;
1947f8f209fSSinan Kaya 
1957f8f209fSSinan Kaya 	name_copy = devm_kstrdup(&dev->pdev->dev, name, GFP_KERNEL);
1967f8f209fSSinan Kaya 	if (!name_copy)
1977f8f209fSSinan Kaya 		return -ENOMEM;
1987f8f209fSSinan Kaya 
1997f8f209fSSinan Kaya 	attrs->attr.name = name_copy;
2007f8f209fSSinan Kaya 	attrs->attr.mode = mode;
2017f8f209fSSinan Kaya 	attrs->show = show_values;
2027f8f209fSSinan Kaya 	attrs->store = set_values;
2037f8f209fSSinan Kaya 	sysfs_attr_init(&attrs->attr);
2047f8f209fSSinan Kaya 
2057f8f209fSSinan Kaya 	return device_create_file(&dev->pdev->dev, attrs);
2067f8f209fSSinan Kaya }
2077f8f209fSSinan Kaya 
create_sysfs_entry_channel(struct hidma_mgmt_dev * mdev,char * name,int mode,int index,struct kobject * parent)2087f8f209fSSinan Kaya static int create_sysfs_entry_channel(struct hidma_mgmt_dev *mdev, char *name,
2097f8f209fSSinan Kaya 				      int mode, int index,
2107f8f209fSSinan Kaya 				      struct kobject *parent)
2117f8f209fSSinan Kaya {
2127f8f209fSSinan Kaya 	struct hidma_chan_attr *chattr;
2137f8f209fSSinan Kaya 	char *name_copy;
2147f8f209fSSinan Kaya 
2157f8f209fSSinan Kaya 	chattr = devm_kmalloc(&mdev->pdev->dev, sizeof(*chattr), GFP_KERNEL);
2167f8f209fSSinan Kaya 	if (!chattr)
2177f8f209fSSinan Kaya 		return -ENOMEM;
2187f8f209fSSinan Kaya 
2197f8f209fSSinan Kaya 	name_copy = devm_kstrdup(&mdev->pdev->dev, name, GFP_KERNEL);
2207f8f209fSSinan Kaya 	if (!name_copy)
2217f8f209fSSinan Kaya 		return -ENOMEM;
2227f8f209fSSinan Kaya 
2237f8f209fSSinan Kaya 	chattr->mdev = mdev;
2247f8f209fSSinan Kaya 	chattr->index = index;
2257f8f209fSSinan Kaya 	chattr->attr.attr.name = name_copy;
2267f8f209fSSinan Kaya 	chattr->attr.attr.mode = mode;
2277f8f209fSSinan Kaya 	chattr->attr.show = show_values_channel;
2287f8f209fSSinan Kaya 	chattr->attr.store = set_values_channel;
2297f8f209fSSinan Kaya 	sysfs_attr_init(&chattr->attr.attr);
2307f8f209fSSinan Kaya 
2317f8f209fSSinan Kaya 	return sysfs_create_file(parent, &chattr->attr.attr);
2327f8f209fSSinan Kaya }
2337f8f209fSSinan Kaya 
hidma_mgmt_init_sys(struct hidma_mgmt_dev * mdev)2347f8f209fSSinan Kaya int hidma_mgmt_init_sys(struct hidma_mgmt_dev *mdev)
2357f8f209fSSinan Kaya {
2367f8f209fSSinan Kaya 	unsigned int i;
2377f8f209fSSinan Kaya 	int rc;
2387f8f209fSSinan Kaya 	int required;
2397f8f209fSSinan Kaya 	struct kobject *chanops;
2407f8f209fSSinan Kaya 
2417f8f209fSSinan Kaya 	required = sizeof(*mdev->chroots) * mdev->dma_channels;
2427f8f209fSSinan Kaya 	mdev->chroots = devm_kmalloc(&mdev->pdev->dev, required, GFP_KERNEL);
2437f8f209fSSinan Kaya 	if (!mdev->chroots)
2447f8f209fSSinan Kaya 		return -ENOMEM;
2457f8f209fSSinan Kaya 
2467f8f209fSSinan Kaya 	chanops = kobject_create_and_add("chanops", &mdev->pdev->dev.kobj);
2477f8f209fSSinan Kaya 	if (!chanops)
2487f8f209fSSinan Kaya 		return -ENOMEM;
2497f8f209fSSinan Kaya 
2507f8f209fSSinan Kaya 	/* create each channel directory here */
2517f8f209fSSinan Kaya 	for (i = 0; i < mdev->dma_channels; i++) {
2527f8f209fSSinan Kaya 		char name[20];
2537f8f209fSSinan Kaya 
2547f8f209fSSinan Kaya 		snprintf(name, sizeof(name), "chan%d", i);
2557f8f209fSSinan Kaya 		mdev->chroots[i] = kobject_create_and_add(name, chanops);
2567f8f209fSSinan Kaya 		if (!mdev->chroots[i])
2577f8f209fSSinan Kaya 			return -ENOMEM;
2587f8f209fSSinan Kaya 	}
2597f8f209fSSinan Kaya 
2607f8f209fSSinan Kaya 	/* populate common parameters */
2617f8f209fSSinan Kaya 	for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {
2627f8f209fSSinan Kaya 		rc = create_sysfs_entry(mdev, hidma_mgmt_files[i].name,
2637f8f209fSSinan Kaya 					hidma_mgmt_files[i].mode);
2647f8f209fSSinan Kaya 		if (rc)
2657f8f209fSSinan Kaya 			return rc;
2667f8f209fSSinan Kaya 	}
2677f8f209fSSinan Kaya 
2687f8f209fSSinan Kaya 	/* populate parameters that are per channel */
2697f8f209fSSinan Kaya 	for (i = 0; i < mdev->dma_channels; i++) {
2707f8f209fSSinan Kaya 		rc = create_sysfs_entry_channel(mdev, "priority",
2717f8f209fSSinan Kaya 						(S_IRUGO | S_IWUGO), i,
2727f8f209fSSinan Kaya 						mdev->chroots[i]);
2737f8f209fSSinan Kaya 		if (rc)
2747f8f209fSSinan Kaya 			return rc;
2757f8f209fSSinan Kaya 
2767f8f209fSSinan Kaya 		rc = create_sysfs_entry_channel(mdev, "weight",
2777f8f209fSSinan Kaya 						(S_IRUGO | S_IWUGO), i,
2787f8f209fSSinan Kaya 						mdev->chroots[i]);
2797f8f209fSSinan Kaya 		if (rc)
2807f8f209fSSinan Kaya 			return rc;
2817f8f209fSSinan Kaya 	}
2827f8f209fSSinan Kaya 
2837f8f209fSSinan Kaya 	return 0;
2847f8f209fSSinan Kaya }
2857f8f209fSSinan Kaya EXPORT_SYMBOL_GPL(hidma_mgmt_init_sys);
286