1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Qualcomm Technologies HIDMA Management SYS interface
4 *
5 * Copyright (c) 2015, The Linux Foundation. All rights reserved.
6 */
7
8#include <linux/sysfs.h>
9#include <linux/platform_device.h>
10
11#include "hidma_mgmt.h"
12
13struct hidma_chan_attr {
14 struct hidma_mgmt_dev *mdev;
15 int index;
16 struct kobj_attribute attr;
17};
18
19struct hidma_mgmt_fileinfo {
20 char *name;
21 int mode;
22 int (*get)(struct hidma_mgmt_dev *mdev);
23 int (*set)(struct hidma_mgmt_dev *mdev, u64 val);
24};
25
26#define IMPLEMENT_GETSET(name) \
27static int get_##name(struct hidma_mgmt_dev *mdev) \
28{ \
29 return mdev->name; \
30} \
31static int set_##name(struct hidma_mgmt_dev *mdev, u64 val) \
32{ \
33 u64 tmp; \
34 int rc; \
35 \
36 tmp = mdev->name; \
37 mdev->name = val; \
38 rc = hidma_mgmt_setup(mdev); \
39 if (rc) \
40 mdev->name = tmp; \
41 return rc; \
42}
43
44#define DECLARE_ATTRIBUTE(name, mode) \
45 {#name, mode, get_##name, set_##name}
46
47IMPLEMENT_GETSET(hw_version_major)
48IMPLEMENT_GETSET(hw_version_minor)
49IMPLEMENT_GETSET(max_wr_xactions)
50IMPLEMENT_GETSET(max_rd_xactions)
51IMPLEMENT_GETSET(max_write_request)
52IMPLEMENT_GETSET(max_read_request)
53IMPLEMENT_GETSET(dma_channels)
54IMPLEMENT_GETSET(chreset_timeout_cycles)
55
56static int set_priority(struct hidma_mgmt_dev *mdev, unsigned int i, u64 val)
57{
58 u64 tmp;
59 int rc;
60
61 if (i >= mdev->dma_channels)
62 return -EINVAL;
63
64 tmp = mdev->priority[i];
65 mdev->priority[i] = val;
66 rc = hidma_mgmt_setup(mgmtdev: mdev);
67 if (rc)
68 mdev->priority[i] = tmp;
69 return rc;
70}
71
72static int set_weight(struct hidma_mgmt_dev *mdev, unsigned int i, u64 val)
73{
74 u64 tmp;
75 int rc;
76
77 if (i >= mdev->dma_channels)
78 return -EINVAL;
79
80 tmp = mdev->weight[i];
81 mdev->weight[i] = val;
82 rc = hidma_mgmt_setup(mgmtdev: mdev);
83 if (rc)
84 mdev->weight[i] = tmp;
85 return rc;
86}
87
88static 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),
97};
98
99static ssize_t show_values(struct device *dev, struct device_attribute *attr,
100 char *buf)
101{
102 struct hidma_mgmt_dev *mdev = dev_get_drvdata(dev);
103 unsigned int i;
104
105 buf[0] = 0;
106
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, fmt: "%d\n", hidma_mgmt_files[i].get(mdev));
110 break;
111 }
112 }
113 return strlen(buf);
114}
115
116static ssize_t set_values(struct device *dev, struct device_attribute *attr,
117 const char *buf, size_t count)
118{
119 struct hidma_mgmt_dev *mdev = dev_get_drvdata(dev);
120 unsigned long tmp;
121 unsigned int i;
122 int rc;
123
124 rc = kstrtoul(s: buf, base: 0, res: &tmp);
125 if (rc)
126 return rc;
127
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);
131 if (rc)
132 return rc;
133
134 break;
135 }
136 }
137 return count;
138}
139
140static ssize_t show_values_channel(struct kobject *kobj,
141 struct kobj_attribute *attr, char *buf)
142{
143 struct hidma_chan_attr *chattr;
144 struct hidma_mgmt_dev *mdev;
145
146 buf[0] = 0;
147 chattr = container_of(attr, struct hidma_chan_attr, attr);
148 mdev = chattr->mdev;
149 if (strcmp(attr->attr.name, "priority") == 0)
150 sprintf(buf, fmt: "%d\n", mdev->priority[chattr->index]);
151 else if (strcmp(attr->attr.name, "weight") == 0)
152 sprintf(buf, fmt: "%d\n", mdev->weight[chattr->index]);
153
154 return strlen(buf);
155}
156
157static ssize_t set_values_channel(struct kobject *kobj,
158 struct kobj_attribute *attr, const char *buf,
159 size_t count)
160{
161 struct hidma_chan_attr *chattr;
162 struct hidma_mgmt_dev *mdev;
163 unsigned long tmp;
164 int rc;
165
166 chattr = container_of(attr, struct hidma_chan_attr, attr);
167 mdev = chattr->mdev;
168
169 rc = kstrtoul(s: buf, base: 0, res: &tmp);
170 if (rc)
171 return rc;
172
173 if (strcmp(attr->attr.name, "priority") == 0) {
174 rc = set_priority(mdev, i: chattr->index, val: tmp);
175 if (rc)
176 return rc;
177 } else if (strcmp(attr->attr.name, "weight") == 0) {
178 rc = set_weight(mdev, i: chattr->index, val: tmp);
179 if (rc)
180 return rc;
181 }
182 return count;
183}
184
185static int create_sysfs_entry(struct hidma_mgmt_dev *dev, char *name, int mode)
186{
187 struct device_attribute *attrs;
188 char *name_copy;
189
190 attrs = devm_kmalloc(dev: &dev->pdev->dev,
191 size: sizeof(struct device_attribute), GFP_KERNEL);
192 if (!attrs)
193 return -ENOMEM;
194
195 name_copy = devm_kstrdup(dev: &dev->pdev->dev, s: name, GFP_KERNEL);
196 if (!name_copy)
197 return -ENOMEM;
198
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);
204
205 return device_create_file(device: &dev->pdev->dev, entry: attrs);
206}
207
208static int create_sysfs_entry_channel(struct hidma_mgmt_dev *mdev, char *name,
209 int mode, int index,
210 struct kobject *parent)
211{
212 struct hidma_chan_attr *chattr;
213 char *name_copy;
214
215 chattr = devm_kmalloc(dev: &mdev->pdev->dev, size: sizeof(*chattr), GFP_KERNEL);
216 if (!chattr)
217 return -ENOMEM;
218
219 name_copy = devm_kstrdup(dev: &mdev->pdev->dev, s: name, GFP_KERNEL);
220 if (!name_copy)
221 return -ENOMEM;
222
223 chattr->mdev = mdev;
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);
230
231 return sysfs_create_file(kobj: parent, attr: &chattr->attr.attr);
232}
233
234int hidma_mgmt_init_sys(struct hidma_mgmt_dev *mdev)
235{
236 unsigned int i;
237 int rc;
238 int required;
239 struct kobject *chanops;
240
241 required = sizeof(*mdev->chroots) * mdev->dma_channels;
242 mdev->chroots = devm_kmalloc(dev: &mdev->pdev->dev, size: required, GFP_KERNEL);
243 if (!mdev->chroots)
244 return -ENOMEM;
245
246 chanops = kobject_create_and_add(name: "chanops", parent: &mdev->pdev->dev.kobj);
247 if (!chanops)
248 return -ENOMEM;
249
250 /* create each channel directory here */
251 for (i = 0; i < mdev->dma_channels; i++) {
252 char name[20];
253
254 snprintf(buf: name, size: sizeof(name), fmt: "chan%d", i);
255 mdev->chroots[i] = kobject_create_and_add(name, parent: chanops);
256 if (!mdev->chroots[i])
257 return -ENOMEM;
258 }
259
260 /* populate common parameters */
261 for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {
262 rc = create_sysfs_entry(dev: mdev, name: hidma_mgmt_files[i].name,
263 mode: hidma_mgmt_files[i].mode);
264 if (rc)
265 return rc;
266 }
267
268 /* populate parameters that are per channel */
269 for (i = 0; i < mdev->dma_channels; i++) {
270 rc = create_sysfs_entry_channel(mdev, name: "priority",
271 mode: (S_IRUGO | S_IWUGO), index: i,
272 parent: mdev->chroots[i]);
273 if (rc)
274 return rc;
275
276 rc = create_sysfs_entry_channel(mdev, name: "weight",
277 mode: (S_IRUGO | S_IWUGO), index: i,
278 parent: mdev->chroots[i]);
279 if (rc)
280 return rc;
281 }
282
283 return 0;
284}
285EXPORT_SYMBOL_GPL(hidma_mgmt_init_sys);
286

source code of linux/drivers/dma/qcom/hidma_mgmt_sys.c