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
13 struct hidma_chan_attr {
14 struct hidma_mgmt_dev *mdev;
15 int index;
16 struct kobj_attribute attr;
17 };
18
19 struct 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) \
27 static int get_##name(struct hidma_mgmt_dev *mdev) \
28 { \
29 return mdev->name; \
30 } \
31 static 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
47 IMPLEMENT_GETSET(hw_version_major)
IMPLEMENT_GETSET(hw_version_minor)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)
55
56 static 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(mdev);
67 if (rc)
68 mdev->priority[i] = tmp;
69 return rc;
70 }
71
set_weight(struct hidma_mgmt_dev * mdev,unsigned int i,u64 val)72 static 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(mdev);
83 if (rc)
84 mdev->weight[i] = tmp;
85 return rc;
86 }
87
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),
97 };
98
show_values(struct device * dev,struct device_attribute * attr,char * buf)99 static 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, "%d\n", hidma_mgmt_files[i].get(mdev));
110 break;
111 }
112 }
113 return strlen(buf);
114 }
115
set_values(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)116 static 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(buf, 0, &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
show_values_channel(struct kobject * kobj,struct kobj_attribute * attr,char * buf)140 static 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, "%d\n", mdev->priority[chattr->index]);
151 else if (strcmp(attr->attr.name, "weight") == 0)
152 sprintf(buf, "%d\n", mdev->weight[chattr->index]);
153
154 return strlen(buf);
155 }
156
set_values_channel(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)157 static 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(buf, 0, &tmp);
170 if (rc)
171 return rc;
172
173 if (strcmp(attr->attr.name, "priority") == 0) {
174 rc = set_priority(mdev, chattr->index, tmp);
175 if (rc)
176 return rc;
177 } else if (strcmp(attr->attr.name, "weight") == 0) {
178 rc = set_weight(mdev, chattr->index, tmp);
179 if (rc)
180 return rc;
181 }
182 return count;
183 }
184
create_sysfs_entry(struct hidma_mgmt_dev * dev,char * name,int mode)185 static 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->pdev->dev,
191 sizeof(struct device_attribute), GFP_KERNEL);
192 if (!attrs)
193 return -ENOMEM;
194
195 name_copy = devm_kstrdup(&dev->pdev->dev, 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(&dev->pdev->dev, attrs);
206 }
207
create_sysfs_entry_channel(struct hidma_mgmt_dev * mdev,char * name,int mode,int index,struct kobject * parent)208 static 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(&mdev->pdev->dev, sizeof(*chattr), GFP_KERNEL);
216 if (!chattr)
217 return -ENOMEM;
218
219 name_copy = devm_kstrdup(&mdev->pdev->dev, 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(parent, &chattr->attr.attr);
232 }
233
hidma_mgmt_init_sys(struct hidma_mgmt_dev * mdev)234 int 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(&mdev->pdev->dev, required, GFP_KERNEL);
243 if (!mdev->chroots)
244 return -ENOMEM;
245
246 chanops = kobject_create_and_add("chanops", &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(name, sizeof(name), "chan%d", i);
255 mdev->chroots[i] = kobject_create_and_add(name, 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(mdev, hidma_mgmt_files[i].name,
263 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, "priority",
271 (S_IRUGO | S_IWUGO), i,
272 mdev->chroots[i]);
273 if (rc)
274 return rc;
275
276 rc = create_sysfs_entry_channel(mdev, "weight",
277 (S_IRUGO | S_IWUGO), i,
278 mdev->chroots[i]);
279 if (rc)
280 return rc;
281 }
282
283 return 0;
284 }
285 EXPORT_SYMBOL_GPL(hidma_mgmt_init_sys);
286