1 // SPDX-License-Identifier: GPL-2.0-only
3 * Qualcomm Technologies HIDMA Management SYS interface
5 * Copyright (c) 2015, The Linux Foundation. All rights reserved.
8 #include <linux/sysfs.h>
9 #include <linux/platform_device.h>
11 #include "hidma_mgmt.h"
13 struct hidma_chan_attr {
14 struct hidma_mgmt_dev *mdev;
16 struct kobj_attribute attr;
19 struct hidma_mgmt_fileinfo {
22 int (*get)(struct hidma_mgmt_dev *mdev);
23 int (*set)(struct hidma_mgmt_dev *mdev, u64 val);
26 #define IMPLEMENT_GETSET(name) \
27 static int get_##name(struct hidma_mgmt_dev *mdev) \
31 static int set_##name(struct hidma_mgmt_dev *mdev, u64 val) \
38 rc = hidma_mgmt_setup(mdev); \
44 #define DECLARE_ATTRIBUTE(name, mode) \
45 {#name, mode, get_##name, set_##name}
47 IMPLEMENT_GETSET(hw_version_major)
48 IMPLEMENT_GETSET(hw_version_minor)
49 IMPLEMENT_GETSET(max_wr_xactions)
50 IMPLEMENT_GETSET(max_rd_xactions)
51 IMPLEMENT_GETSET(max_write_request)
52 IMPLEMENT_GETSET(max_read_request)
53 IMPLEMENT_GETSET(dma_channels)
54 IMPLEMENT_GETSET(chreset_timeout_cycles)
56 static int set_priority(struct hidma_mgmt_dev *mdev, unsigned int i, u64 val)
61 if (i >= mdev->dma_channels)
64 tmp = mdev->priority[i];
65 mdev->priority[i] = val;
66 rc = hidma_mgmt_setup(mdev);
68 mdev->priority[i] = tmp;
72 static int set_weight(struct hidma_mgmt_dev *mdev, unsigned int i, u64 val)
77 if (i >= mdev->dma_channels)
80 tmp = mdev->weight[i];
81 mdev->weight[i] = val;
82 rc = hidma_mgmt_setup(mdev);
84 mdev->weight[i] = tmp;
88 static struct hidma_mgmt_fileinfo hidma_mgmt_files[] = {
89 DECLARE_ATTRIBUTE(hw_version_major, S_IRUGO),
90 DECLARE_ATTRIBUTE(hw_version_minor, S_IRUGO),
91 DECLARE_ATTRIBUTE(dma_channels, S_IRUGO),
92 DECLARE_ATTRIBUTE(chreset_timeout_cycles, S_IRUGO),
93 DECLARE_ATTRIBUTE(max_wr_xactions, S_IRUGO),
94 DECLARE_ATTRIBUTE(max_rd_xactions, S_IRUGO),
95 DECLARE_ATTRIBUTE(max_write_request, S_IRUGO),
96 DECLARE_ATTRIBUTE(max_read_request, S_IRUGO),
99 static ssize_t show_values(struct device *dev, struct device_attribute *attr,
102 struct hidma_mgmt_dev *mdev = dev_get_drvdata(dev);
107 for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {
108 if (strcmp(attr->attr.name, hidma_mgmt_files[i].name) == 0) {
109 sprintf(buf, "%d\n", hidma_mgmt_files[i].get(mdev));
116 static ssize_t set_values(struct device *dev, struct device_attribute *attr,
117 const char *buf, size_t count)
119 struct hidma_mgmt_dev *mdev = dev_get_drvdata(dev);
124 rc = kstrtoul(buf, 0, &tmp);
128 for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {
129 if (strcmp(attr->attr.name, hidma_mgmt_files[i].name) == 0) {
130 rc = hidma_mgmt_files[i].set(mdev, tmp);
140 static ssize_t show_values_channel(struct kobject *kobj,
141 struct kobj_attribute *attr, char *buf)
143 struct hidma_chan_attr *chattr;
144 struct hidma_mgmt_dev *mdev;
147 chattr = container_of(attr, struct hidma_chan_attr, attr);
149 if (strcmp(attr->attr.name, "priority") == 0)
150 sprintf(buf, "%d\n", mdev->priority[chattr->index]);
151 else if (strcmp(attr->attr.name, "weight") == 0)
152 sprintf(buf, "%d\n", mdev->weight[chattr->index]);
157 static ssize_t set_values_channel(struct kobject *kobj,
158 struct kobj_attribute *attr, const char *buf,
161 struct hidma_chan_attr *chattr;
162 struct hidma_mgmt_dev *mdev;
166 chattr = container_of(attr, struct hidma_chan_attr, attr);
169 rc = kstrtoul(buf, 0, &tmp);
173 if (strcmp(attr->attr.name, "priority") == 0) {
174 rc = set_priority(mdev, chattr->index, tmp);
177 } else if (strcmp(attr->attr.name, "weight") == 0) {
178 rc = set_weight(mdev, chattr->index, tmp);
185 static int create_sysfs_entry(struct hidma_mgmt_dev *dev, char *name, int mode)
187 struct device_attribute *attrs;
190 attrs = devm_kmalloc(&dev->pdev->dev,
191 sizeof(struct device_attribute), GFP_KERNEL);
195 name_copy = devm_kstrdup(&dev->pdev->dev, name, GFP_KERNEL);
199 attrs->attr.name = name_copy;
200 attrs->attr.mode = mode;
201 attrs->show = show_values;
202 attrs->store = set_values;
203 sysfs_attr_init(&attrs->attr);
205 return device_create_file(&dev->pdev->dev, attrs);
208 static int create_sysfs_entry_channel(struct hidma_mgmt_dev *mdev, char *name,
210 struct kobject *parent)
212 struct hidma_chan_attr *chattr;
215 chattr = devm_kmalloc(&mdev->pdev->dev, sizeof(*chattr), GFP_KERNEL);
219 name_copy = devm_kstrdup(&mdev->pdev->dev, name, GFP_KERNEL);
224 chattr->index = index;
225 chattr->attr.attr.name = name_copy;
226 chattr->attr.attr.mode = mode;
227 chattr->attr.show = show_values_channel;
228 chattr->attr.store = set_values_channel;
229 sysfs_attr_init(&chattr->attr.attr);
231 return sysfs_create_file(parent, &chattr->attr.attr);
234 int hidma_mgmt_init_sys(struct hidma_mgmt_dev *mdev)
239 struct kobject *chanops;
241 required = sizeof(*mdev->chroots) * mdev->dma_channels;
242 mdev->chroots = devm_kmalloc(&mdev->pdev->dev, required, GFP_KERNEL);
246 chanops = kobject_create_and_add("chanops", &mdev->pdev->dev.kobj);
250 /* create each channel directory here */
251 for (i = 0; i < mdev->dma_channels; i++) {
254 snprintf(name, sizeof(name), "chan%d", i);
255 mdev->chroots[i] = kobject_create_and_add(name, chanops);
256 if (!mdev->chroots[i])
260 /* populate common parameters */
261 for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {
262 rc = create_sysfs_entry(mdev, hidma_mgmt_files[i].name,
263 hidma_mgmt_files[i].mode);
268 /* populate parameters that are per channel */
269 for (i = 0; i < mdev->dma_channels; i++) {
270 rc = create_sysfs_entry_channel(mdev, "priority",
271 (S_IRUGO | S_IWUGO), i,
276 rc = create_sysfs_entry_channel(mdev, "weight",
277 (S_IRUGO | S_IWUGO), i,
285 EXPORT_SYMBOL_GPL(hidma_mgmt_init_sys);