GNU Linux-libre 4.9.287-gnu1
[releases.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 platform_device *pdev = to_platform_device(dev);
111         struct hidma_mgmt_dev *mdev = platform_get_drvdata(pdev);
112         unsigned int i;
113
114         buf[0] = 0;
115
116         for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {
117                 if (strcmp(attr->attr.name, hidma_mgmt_files[i].name) == 0) {
118                         sprintf(buf, "%d\n", hidma_mgmt_files[i].get(mdev));
119                         break;
120                 }
121         }
122         return strlen(buf);
123 }
124
125 static ssize_t set_values(struct device *dev, struct device_attribute *attr,
126                           const char *buf, size_t count)
127 {
128         struct platform_device *pdev = to_platform_device(dev);
129         struct hidma_mgmt_dev *mdev = platform_get_drvdata(pdev);
130         unsigned long tmp;
131         unsigned int i;
132         int rc;
133
134         rc = kstrtoul(buf, 0, &tmp);
135         if (rc)
136                 return rc;
137
138         for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {
139                 if (strcmp(attr->attr.name, hidma_mgmt_files[i].name) == 0) {
140                         rc = hidma_mgmt_files[i].set(mdev, tmp);
141                         if (rc)
142                                 return rc;
143
144                         break;
145                 }
146         }
147         return count;
148 }
149
150 static ssize_t show_values_channel(struct kobject *kobj,
151                                    struct kobj_attribute *attr, char *buf)
152 {
153         struct hidma_chan_attr *chattr;
154         struct hidma_mgmt_dev *mdev;
155
156         buf[0] = 0;
157         chattr = container_of(attr, struct hidma_chan_attr, attr);
158         mdev = chattr->mdev;
159         if (strcmp(attr->attr.name, "priority") == 0)
160                 sprintf(buf, "%d\n", mdev->priority[chattr->index]);
161         else if (strcmp(attr->attr.name, "weight") == 0)
162                 sprintf(buf, "%d\n", mdev->weight[chattr->index]);
163
164         return strlen(buf);
165 }
166
167 static ssize_t set_values_channel(struct kobject *kobj,
168                                   struct kobj_attribute *attr, const char *buf,
169                                   size_t count)
170 {
171         struct hidma_chan_attr *chattr;
172         struct hidma_mgmt_dev *mdev;
173         unsigned long tmp;
174         int rc;
175
176         chattr = container_of(attr, struct hidma_chan_attr, attr);
177         mdev = chattr->mdev;
178
179         rc = kstrtoul(buf, 0, &tmp);
180         if (rc)
181                 return rc;
182
183         if (strcmp(attr->attr.name, "priority") == 0) {
184                 rc = set_priority(mdev, chattr->index, tmp);
185                 if (rc)
186                         return rc;
187         } else if (strcmp(attr->attr.name, "weight") == 0) {
188                 rc = set_weight(mdev, chattr->index, tmp);
189                 if (rc)
190                         return rc;
191         }
192         return count;
193 }
194
195 static int create_sysfs_entry(struct hidma_mgmt_dev *dev, char *name, int mode)
196 {
197         struct device_attribute *attrs;
198         char *name_copy;
199
200         attrs = devm_kmalloc(&dev->pdev->dev,
201                              sizeof(struct device_attribute), GFP_KERNEL);
202         if (!attrs)
203                 return -ENOMEM;
204
205         name_copy = devm_kstrdup(&dev->pdev->dev, name, GFP_KERNEL);
206         if (!name_copy)
207                 return -ENOMEM;
208
209         attrs->attr.name = name_copy;
210         attrs->attr.mode = mode;
211         attrs->show = show_values;
212         attrs->store = set_values;
213         sysfs_attr_init(&attrs->attr);
214
215         return device_create_file(&dev->pdev->dev, attrs);
216 }
217
218 static int create_sysfs_entry_channel(struct hidma_mgmt_dev *mdev, char *name,
219                                       int mode, int index,
220                                       struct kobject *parent)
221 {
222         struct hidma_chan_attr *chattr;
223         char *name_copy;
224
225         chattr = devm_kmalloc(&mdev->pdev->dev, sizeof(*chattr), GFP_KERNEL);
226         if (!chattr)
227                 return -ENOMEM;
228
229         name_copy = devm_kstrdup(&mdev->pdev->dev, name, GFP_KERNEL);
230         if (!name_copy)
231                 return -ENOMEM;
232
233         chattr->mdev = mdev;
234         chattr->index = index;
235         chattr->attr.attr.name = name_copy;
236         chattr->attr.attr.mode = mode;
237         chattr->attr.show = show_values_channel;
238         chattr->attr.store = set_values_channel;
239         sysfs_attr_init(&chattr->attr.attr);
240
241         return sysfs_create_file(parent, &chattr->attr.attr);
242 }
243
244 int hidma_mgmt_init_sys(struct hidma_mgmt_dev *mdev)
245 {
246         unsigned int i;
247         int rc;
248         int required;
249         struct kobject *chanops;
250
251         required = sizeof(*mdev->chroots) * mdev->dma_channels;
252         mdev->chroots = devm_kmalloc(&mdev->pdev->dev, required, GFP_KERNEL);
253         if (!mdev->chroots)
254                 return -ENOMEM;
255
256         chanops = kobject_create_and_add("chanops", &mdev->pdev->dev.kobj);
257         if (!chanops)
258                 return -ENOMEM;
259
260         /* create each channel directory here */
261         for (i = 0; i < mdev->dma_channels; i++) {
262                 char name[20];
263
264                 snprintf(name, sizeof(name), "chan%d", i);
265                 mdev->chroots[i] = kobject_create_and_add(name, chanops);
266                 if (!mdev->chroots[i])
267                         return -ENOMEM;
268         }
269
270         /* populate common parameters */
271         for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {
272                 rc = create_sysfs_entry(mdev, hidma_mgmt_files[i].name,
273                                         hidma_mgmt_files[i].mode);
274                 if (rc)
275                         return rc;
276         }
277
278         /* populate parameters that are per channel */
279         for (i = 0; i < mdev->dma_channels; i++) {
280                 rc = create_sysfs_entry_channel(mdev, "priority",
281                                                 (S_IRUGO | S_IWUGO), i,
282                                                 mdev->chroots[i]);
283                 if (rc)
284                         return rc;
285
286                 rc = create_sysfs_entry_channel(mdev, "weight",
287                                                 (S_IRUGO | S_IWUGO), i,
288                                                 mdev->chroots[i]);
289                 if (rc)
290                         return rc;
291         }
292
293         return 0;
294 }
295 EXPORT_SYMBOL_GPL(hidma_mgmt_init_sys);