Linux-libre 4.19.123-gnu
[librecmc/linux-libre.git] / drivers / dma / qcom / hidma_mgmt_sys.c
1 /*
2  * Qualcomm Technologies HIDMA Management SYS interface
3  *
4  * Copyright (c) 2015, The Linux Foundation. All rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 and
8  * only version 2 as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  */
15
16 #include <linux/sysfs.h>
17 #include <linux/platform_device.h>
18
19 #include "hidma_mgmt.h"
20
21 struct hidma_chan_attr {
22         struct hidma_mgmt_dev *mdev;
23         int index;
24         struct kobj_attribute attr;
25 };
26
27 struct hidma_mgmt_fileinfo {
28         char *name;
29         int mode;
30         int (*get)(struct hidma_mgmt_dev *mdev);
31         int (*set)(struct hidma_mgmt_dev *mdev, u64 val);
32 };
33
34 #define IMPLEMENT_GETSET(name)                                  \
35 static int get_##name(struct hidma_mgmt_dev *mdev)              \
36 {                                                               \
37         return mdev->name;                                      \
38 }                                                               \
39 static int set_##name(struct hidma_mgmt_dev *mdev, u64 val)     \
40 {                                                               \
41         u64 tmp;                                                \
42         int rc;                                                 \
43                                                                 \
44         tmp = mdev->name;                                       \
45         mdev->name = val;                                       \
46         rc = hidma_mgmt_setup(mdev);                            \
47         if (rc)                                                 \
48                 mdev->name = tmp;                               \
49         return rc;                                              \
50 }
51
52 #define DECLARE_ATTRIBUTE(name, mode)                           \
53         {#name, mode, get_##name, set_##name}
54
55 IMPLEMENT_GETSET(hw_version_major)
56 IMPLEMENT_GETSET(hw_version_minor)
57 IMPLEMENT_GETSET(max_wr_xactions)
58 IMPLEMENT_GETSET(max_rd_xactions)
59 IMPLEMENT_GETSET(max_write_request)
60 IMPLEMENT_GETSET(max_read_request)
61 IMPLEMENT_GETSET(dma_channels)
62 IMPLEMENT_GETSET(chreset_timeout_cycles)
63
64 static int set_priority(struct hidma_mgmt_dev *mdev, unsigned int i, u64 val)
65 {
66         u64 tmp;
67         int rc;
68
69         if (i >= mdev->dma_channels)
70                 return -EINVAL;
71
72         tmp = mdev->priority[i];
73         mdev->priority[i] = val;
74         rc = hidma_mgmt_setup(mdev);
75         if (rc)
76                 mdev->priority[i] = tmp;
77         return rc;
78 }
79
80 static int set_weight(struct hidma_mgmt_dev *mdev, unsigned int i, u64 val)
81 {
82         u64 tmp;
83         int rc;
84
85         if (i >= mdev->dma_channels)
86                 return -EINVAL;
87
88         tmp = mdev->weight[i];
89         mdev->weight[i] = val;
90         rc = hidma_mgmt_setup(mdev);
91         if (rc)
92                 mdev->weight[i] = tmp;
93         return rc;
94 }
95
96 static struct hidma_mgmt_fileinfo hidma_mgmt_files[] = {
97         DECLARE_ATTRIBUTE(hw_version_major, S_IRUGO),
98         DECLARE_ATTRIBUTE(hw_version_minor, S_IRUGO),
99         DECLARE_ATTRIBUTE(dma_channels, S_IRUGO),
100         DECLARE_ATTRIBUTE(chreset_timeout_cycles, S_IRUGO),
101         DECLARE_ATTRIBUTE(max_wr_xactions, S_IRUGO),
102         DECLARE_ATTRIBUTE(max_rd_xactions, S_IRUGO),
103         DECLARE_ATTRIBUTE(max_write_request, S_IRUGO),
104         DECLARE_ATTRIBUTE(max_read_request, S_IRUGO),
105 };
106
107 static ssize_t show_values(struct device *dev, struct device_attribute *attr,
108                            char *buf)
109 {
110         struct hidma_mgmt_dev *mdev = dev_get_drvdata(dev);
111         unsigned int i;
112
113         buf[0] = 0;
114
115         for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {
116                 if (strcmp(attr->attr.name, hidma_mgmt_files[i].name) == 0) {
117                         sprintf(buf, "%d\n", hidma_mgmt_files[i].get(mdev));
118                         break;
119                 }
120         }
121         return strlen(buf);
122 }
123
124 static ssize_t set_values(struct device *dev, struct device_attribute *attr,
125                           const char *buf, size_t count)
126 {
127         struct hidma_mgmt_dev *mdev = dev_get_drvdata(dev);
128         unsigned long tmp;
129         unsigned int i;
130         int rc;
131
132         rc = kstrtoul(buf, 0, &tmp);
133         if (rc)
134                 return rc;
135
136         for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {
137                 if (strcmp(attr->attr.name, hidma_mgmt_files[i].name) == 0) {
138                         rc = hidma_mgmt_files[i].set(mdev, tmp);
139                         if (rc)
140                                 return rc;
141
142                         break;
143                 }
144         }
145         return count;
146 }
147
148 static ssize_t show_values_channel(struct kobject *kobj,
149                                    struct kobj_attribute *attr, char *buf)
150 {
151         struct hidma_chan_attr *chattr;
152         struct hidma_mgmt_dev *mdev;
153
154         buf[0] = 0;
155         chattr = container_of(attr, struct hidma_chan_attr, attr);
156         mdev = chattr->mdev;
157         if (strcmp(attr->attr.name, "priority") == 0)
158                 sprintf(buf, "%d\n", mdev->priority[chattr->index]);
159         else if (strcmp(attr->attr.name, "weight") == 0)
160                 sprintf(buf, "%d\n", mdev->weight[chattr->index]);
161
162         return strlen(buf);
163 }
164
165 static ssize_t set_values_channel(struct kobject *kobj,
166                                   struct kobj_attribute *attr, const char *buf,
167                                   size_t count)
168 {
169         struct hidma_chan_attr *chattr;
170         struct hidma_mgmt_dev *mdev;
171         unsigned long tmp;
172         int rc;
173
174         chattr = container_of(attr, struct hidma_chan_attr, attr);
175         mdev = chattr->mdev;
176
177         rc = kstrtoul(buf, 0, &tmp);
178         if (rc)
179                 return rc;
180
181         if (strcmp(attr->attr.name, "priority") == 0) {
182                 rc = set_priority(mdev, chattr->index, tmp);
183                 if (rc)
184                         return rc;
185         } else if (strcmp(attr->attr.name, "weight") == 0) {
186                 rc = set_weight(mdev, chattr->index, tmp);
187                 if (rc)
188                         return rc;
189         }
190         return count;
191 }
192
193 static int create_sysfs_entry(struct hidma_mgmt_dev *dev, char *name, int mode)
194 {
195         struct device_attribute *attrs;
196         char *name_copy;
197
198         attrs = devm_kmalloc(&dev->pdev->dev,
199                              sizeof(struct device_attribute), GFP_KERNEL);
200         if (!attrs)
201                 return -ENOMEM;
202
203         name_copy = devm_kstrdup(&dev->pdev->dev, name, GFP_KERNEL);
204         if (!name_copy)
205                 return -ENOMEM;
206
207         attrs->attr.name = name_copy;
208         attrs->attr.mode = mode;
209         attrs->show = show_values;
210         attrs->store = set_values;
211         sysfs_attr_init(&attrs->attr);
212
213         return device_create_file(&dev->pdev->dev, attrs);
214 }
215
216 static int create_sysfs_entry_channel(struct hidma_mgmt_dev *mdev, char *name,
217                                       int mode, int index,
218                                       struct kobject *parent)
219 {
220         struct hidma_chan_attr *chattr;
221         char *name_copy;
222
223         chattr = devm_kmalloc(&mdev->pdev->dev, sizeof(*chattr), GFP_KERNEL);
224         if (!chattr)
225                 return -ENOMEM;
226
227         name_copy = devm_kstrdup(&mdev->pdev->dev, name, GFP_KERNEL);
228         if (!name_copy)
229                 return -ENOMEM;
230
231         chattr->mdev = mdev;
232         chattr->index = index;
233         chattr->attr.attr.name = name_copy;
234         chattr->attr.attr.mode = mode;
235         chattr->attr.show = show_values_channel;
236         chattr->attr.store = set_values_channel;
237         sysfs_attr_init(&chattr->attr.attr);
238
239         return sysfs_create_file(parent, &chattr->attr.attr);
240 }
241
242 int hidma_mgmt_init_sys(struct hidma_mgmt_dev *mdev)
243 {
244         unsigned int i;
245         int rc;
246         int required;
247         struct kobject *chanops;
248
249         required = sizeof(*mdev->chroots) * mdev->dma_channels;
250         mdev->chroots = devm_kmalloc(&mdev->pdev->dev, required, GFP_KERNEL);
251         if (!mdev->chroots)
252                 return -ENOMEM;
253
254         chanops = kobject_create_and_add("chanops", &mdev->pdev->dev.kobj);
255         if (!chanops)
256                 return -ENOMEM;
257
258         /* create each channel directory here */
259         for (i = 0; i < mdev->dma_channels; i++) {
260                 char name[20];
261
262                 snprintf(name, sizeof(name), "chan%d", i);
263                 mdev->chroots[i] = kobject_create_and_add(name, chanops);
264                 if (!mdev->chroots[i])
265                         return -ENOMEM;
266         }
267
268         /* populate common parameters */
269         for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {
270                 rc = create_sysfs_entry(mdev, hidma_mgmt_files[i].name,
271                                         hidma_mgmt_files[i].mode);
272                 if (rc)
273                         return rc;
274         }
275
276         /* populate parameters that are per channel */
277         for (i = 0; i < mdev->dma_channels; i++) {
278                 rc = create_sysfs_entry_channel(mdev, "priority",
279                                                 (S_IRUGO | S_IWUGO), i,
280                                                 mdev->chroots[i]);
281                 if (rc)
282                         return rc;
283
284                 rc = create_sysfs_entry_channel(mdev, "weight",
285                                                 (S_IRUGO | S_IWUGO), i,
286                                                 mdev->chroots[i]);
287                 if (rc)
288                         return rc;
289         }
290
291         return 0;
292 }
293 EXPORT_SYMBOL_GPL(hidma_mgmt_init_sys);