aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/cxl/sysfs.c
diff options
context:
space:
mode:
authorIan Munsie <imunsie@au1.ibm.com>2014-10-08 04:55:02 -0400
committerMichael Ellerman <mpe@ellerman.id.au>2014-10-08 05:15:57 -0400
commitf204e0b8cedd7da1dfcfd05ed6b7692737e24029 (patch)
tree35ca15049345cdd5dbed38229a6b3add05610658 /drivers/misc/cxl/sysfs.c
parent10542ca0156f60571ef41799d44d40dd4cb0a473 (diff)
cxl: Driver code for powernv PCIe based cards for userspace access
This is the core of the cxl driver. It adds support for using cxl cards in the powernv environment only (ie POWER8 bare metal). It allows access to cxl accelerators by userspace using the /dev/cxl/afuM.N char devices. The kernel driver has no knowledge of the function implemented by the accelerator. It provides services to userspace via the /dev/cxl/afuM.N devices. When a program opens this device and runs the start work IOCTL, the accelerator will have coherent access to that processes memory using the same virtual addresses. That process may mmap the device to access any MMIO space the accelerator provides. Also, reads on the device will allow interrupts to be received. These services are further documented in a later patch in Documentation/powerpc/cxl.txt. Documentation of the cxl hardware architecture and userspace API is provided in subsequent patches. Signed-off-by: Ian Munsie <imunsie@au1.ibm.com> Signed-off-by: Michael Neuling <mikey@neuling.org> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Diffstat (limited to 'drivers/misc/cxl/sysfs.c')
-rw-r--r--drivers/misc/cxl/sysfs.c385
1 files changed, 385 insertions, 0 deletions
diff --git a/drivers/misc/cxl/sysfs.c b/drivers/misc/cxl/sysfs.c
new file mode 100644
index 000000000000..ce7ec06d87d1
--- /dev/null
+++ b/drivers/misc/cxl/sysfs.c
@@ -0,0 +1,385 @@
1/*
2 * Copyright 2014 IBM Corp.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 */
9
10#include <linux/kernel.h>
11#include <linux/device.h>
12#include <linux/sysfs.h>
13
14#include "cxl.h"
15
16#define to_afu_chardev_m(d) dev_get_drvdata(d)
17
18/********* Adapter attributes **********************************************/
19
20static ssize_t caia_version_show(struct device *device,
21 struct device_attribute *attr,
22 char *buf)
23{
24 struct cxl *adapter = to_cxl_adapter(device);
25
26 return scnprintf(buf, PAGE_SIZE, "%i.%i\n", adapter->caia_major,
27 adapter->caia_minor);
28}
29
30static ssize_t psl_revision_show(struct device *device,
31 struct device_attribute *attr,
32 char *buf)
33{
34 struct cxl *adapter = to_cxl_adapter(device);
35
36 return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->psl_rev);
37}
38
39static ssize_t base_image_show(struct device *device,
40 struct device_attribute *attr,
41 char *buf)
42{
43 struct cxl *adapter = to_cxl_adapter(device);
44
45 return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->base_image);
46}
47
48static ssize_t image_loaded_show(struct device *device,
49 struct device_attribute *attr,
50 char *buf)
51{
52 struct cxl *adapter = to_cxl_adapter(device);
53
54 if (adapter->user_image_loaded)
55 return scnprintf(buf, PAGE_SIZE, "user\n");
56 return scnprintf(buf, PAGE_SIZE, "factory\n");
57}
58
59static struct device_attribute adapter_attrs[] = {
60 __ATTR_RO(caia_version),
61 __ATTR_RO(psl_revision),
62 __ATTR_RO(base_image),
63 __ATTR_RO(image_loaded),
64};
65
66
67/********* AFU master specific attributes **********************************/
68
69static ssize_t mmio_size_show_master(struct device *device,
70 struct device_attribute *attr,
71 char *buf)
72{
73 struct cxl_afu *afu = to_afu_chardev_m(device);
74
75 return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->adapter->ps_size);
76}
77
78static ssize_t pp_mmio_off_show(struct device *device,
79 struct device_attribute *attr,
80 char *buf)
81{
82 struct cxl_afu *afu = to_afu_chardev_m(device);
83
84 return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->pp_offset);
85}
86
87static ssize_t pp_mmio_len_show(struct device *device,
88 struct device_attribute *attr,
89 char *buf)
90{
91 struct cxl_afu *afu = to_afu_chardev_m(device);
92
93 return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->pp_size);
94}
95
96static struct device_attribute afu_master_attrs[] = {
97 __ATTR(mmio_size, S_IRUGO, mmio_size_show_master, NULL),
98 __ATTR_RO(pp_mmio_off),
99 __ATTR_RO(pp_mmio_len),
100};
101
102
103/********* AFU attributes **************************************************/
104
105static ssize_t mmio_size_show(struct device *device,
106 struct device_attribute *attr,
107 char *buf)
108{
109 struct cxl_afu *afu = to_cxl_afu(device);
110
111 if (afu->pp_size)
112 return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->pp_size);
113 return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->adapter->ps_size);
114}
115
116static ssize_t reset_store_afu(struct device *device,
117 struct device_attribute *attr,
118 const char *buf, size_t count)
119{
120 struct cxl_afu *afu = to_cxl_afu(device);
121 int rc;
122
123 /* Not safe to reset if it is currently in use */
124 spin_lock(&afu->contexts_lock);
125 if (!idr_is_empty(&afu->contexts_idr)) {
126 rc = -EBUSY;
127 goto err;
128 }
129
130 if ((rc = cxl_afu_reset(afu)))
131 goto err;
132
133 rc = count;
134err:
135 spin_unlock(&afu->contexts_lock);
136 return rc;
137}
138
139static ssize_t irqs_min_show(struct device *device,
140 struct device_attribute *attr,
141 char *buf)
142{
143 struct cxl_afu *afu = to_cxl_afu(device);
144
145 return scnprintf(buf, PAGE_SIZE, "%i\n", afu->pp_irqs);
146}
147
148static ssize_t irqs_max_show(struct device *device,
149 struct device_attribute *attr,
150 char *buf)
151{
152 struct cxl_afu *afu = to_cxl_afu(device);
153
154 return scnprintf(buf, PAGE_SIZE, "%i\n", afu->irqs_max);
155}
156
157static ssize_t irqs_max_store(struct device *device,
158 struct device_attribute *attr,
159 const char *buf, size_t count)
160{
161 struct cxl_afu *afu = to_cxl_afu(device);
162 ssize_t ret;
163 int irqs_max;
164
165 ret = sscanf(buf, "%i", &irqs_max);
166 if (ret != 1)
167 return -EINVAL;
168
169 if (irqs_max < afu->pp_irqs)
170 return -EINVAL;
171
172 if (irqs_max > afu->adapter->user_irqs)
173 return -EINVAL;
174
175 afu->irqs_max = irqs_max;
176 return count;
177}
178
179static ssize_t modes_supported_show(struct device *device,
180 struct device_attribute *attr, char *buf)
181{
182 struct cxl_afu *afu = to_cxl_afu(device);
183 char *p = buf, *end = buf + PAGE_SIZE;
184
185 if (afu->modes_supported & CXL_MODE_DEDICATED)
186 p += scnprintf(p, end - p, "dedicated_process\n");
187 if (afu->modes_supported & CXL_MODE_DIRECTED)
188 p += scnprintf(p, end - p, "afu_directed\n");
189 return (p - buf);
190}
191
192static ssize_t prefault_mode_show(struct device *device,
193 struct device_attribute *attr,
194 char *buf)
195{
196 struct cxl_afu *afu = to_cxl_afu(device);
197
198 switch (afu->prefault_mode) {
199 case CXL_PREFAULT_WED:
200 return scnprintf(buf, PAGE_SIZE, "work_element_descriptor\n");
201 case CXL_PREFAULT_ALL:
202 return scnprintf(buf, PAGE_SIZE, "all\n");
203 default:
204 return scnprintf(buf, PAGE_SIZE, "none\n");
205 }
206}
207
208static ssize_t prefault_mode_store(struct device *device,
209 struct device_attribute *attr,
210 const char *buf, size_t count)
211{
212 struct cxl_afu *afu = to_cxl_afu(device);
213 enum prefault_modes mode = -1;
214
215 if (!strncmp(buf, "work_element_descriptor", 23))
216 mode = CXL_PREFAULT_WED;
217 if (!strncmp(buf, "all", 3))
218 mode = CXL_PREFAULT_ALL;
219 if (!strncmp(buf, "none", 4))
220 mode = CXL_PREFAULT_NONE;
221
222 if (mode == -1)
223 return -EINVAL;
224
225 afu->prefault_mode = mode;
226 return count;
227}
228
229static ssize_t mode_show(struct device *device,
230 struct device_attribute *attr,
231 char *buf)
232{
233 struct cxl_afu *afu = to_cxl_afu(device);
234
235 if (afu->current_mode == CXL_MODE_DEDICATED)
236 return scnprintf(buf, PAGE_SIZE, "dedicated_process\n");
237 if (afu->current_mode == CXL_MODE_DIRECTED)
238 return scnprintf(buf, PAGE_SIZE, "afu_directed\n");
239 return scnprintf(buf, PAGE_SIZE, "none\n");
240}
241
242static ssize_t mode_store(struct device *device, struct device_attribute *attr,
243 const char *buf, size_t count)
244{
245 struct cxl_afu *afu = to_cxl_afu(device);
246 int old_mode, mode = -1;
247 int rc = -EBUSY;
248
249 /* can't change this if we have a user */
250 spin_lock(&afu->contexts_lock);
251 if (!idr_is_empty(&afu->contexts_idr))
252 goto err;
253
254 if (!strncmp(buf, "dedicated_process", 17))
255 mode = CXL_MODE_DEDICATED;
256 if (!strncmp(buf, "afu_directed", 12))
257 mode = CXL_MODE_DIRECTED;
258 if (!strncmp(buf, "none", 4))
259 mode = 0;
260
261 if (mode == -1) {
262 rc = -EINVAL;
263 goto err;
264 }
265
266 /*
267 * cxl_afu_deactivate_mode needs to be done outside the lock, prevent
268 * other contexts coming in before we are ready:
269 */
270 old_mode = afu->current_mode;
271 afu->current_mode = 0;
272 afu->num_procs = 0;
273
274 spin_unlock(&afu->contexts_lock);
275
276 if ((rc = _cxl_afu_deactivate_mode(afu, old_mode)))
277 return rc;
278 if ((rc = cxl_afu_activate_mode(afu, mode)))
279 return rc;
280
281 return count;
282err:
283 spin_unlock(&afu->contexts_lock);
284 return rc;
285}
286
287static ssize_t api_version_show(struct device *device,
288 struct device_attribute *attr,
289 char *buf)
290{
291 return scnprintf(buf, PAGE_SIZE, "%i\n", CXL_API_VERSION);
292}
293
294static ssize_t api_version_compatible_show(struct device *device,
295 struct device_attribute *attr,
296 char *buf)
297{
298 return scnprintf(buf, PAGE_SIZE, "%i\n", CXL_API_VERSION_COMPATIBLE);
299}
300
301static struct device_attribute afu_attrs[] = {
302 __ATTR_RO(mmio_size),
303 __ATTR_RO(irqs_min),
304 __ATTR_RW(irqs_max),
305 __ATTR_RO(modes_supported),
306 __ATTR_RW(mode),
307 __ATTR_RW(prefault_mode),
308 __ATTR_RO(api_version),
309 __ATTR_RO(api_version_compatible),
310 __ATTR(reset, S_IWUSR, NULL, reset_store_afu),
311};
312
313
314
315int cxl_sysfs_adapter_add(struct cxl *adapter)
316{
317 int i, rc;
318
319 for (i = 0; i < ARRAY_SIZE(adapter_attrs); i++) {
320 if ((rc = device_create_file(&adapter->dev, &adapter_attrs[i])))
321 goto err;
322 }
323 return 0;
324err:
325 for (i--; i >= 0; i--)
326 device_remove_file(&adapter->dev, &adapter_attrs[i]);
327 return rc;
328}
329void cxl_sysfs_adapter_remove(struct cxl *adapter)
330{
331 int i;
332
333 for (i = 0; i < ARRAY_SIZE(adapter_attrs); i++)
334 device_remove_file(&adapter->dev, &adapter_attrs[i]);
335}
336
337int cxl_sysfs_afu_add(struct cxl_afu *afu)
338{
339 int i, rc;
340
341 for (i = 0; i < ARRAY_SIZE(afu_attrs); i++) {
342 if ((rc = device_create_file(&afu->dev, &afu_attrs[i])))
343 goto err;
344 }
345
346 return 0;
347
348err:
349 for (i--; i >= 0; i--)
350 device_remove_file(&afu->dev, &afu_attrs[i]);
351 return rc;
352}
353
354void cxl_sysfs_afu_remove(struct cxl_afu *afu)
355{
356 int i;
357
358 for (i = 0; i < ARRAY_SIZE(afu_attrs); i++)
359 device_remove_file(&afu->dev, &afu_attrs[i]);
360}
361
362int cxl_sysfs_afu_m_add(struct cxl_afu *afu)
363{
364 int i, rc;
365
366 for (i = 0; i < ARRAY_SIZE(afu_master_attrs); i++) {
367 if ((rc = device_create_file(afu->chardev_m, &afu_master_attrs[i])))
368 goto err;
369 }
370
371 return 0;
372
373err:
374 for (i--; i >= 0; i--)
375 device_remove_file(afu->chardev_m, &afu_master_attrs[i]);
376 return rc;
377}
378
379void cxl_sysfs_afu_m_remove(struct cxl_afu *afu)
380{
381 int i;
382
383 for (i = 0; i < ARRAY_SIZE(afu_master_attrs); i++)
384 device_remove_file(afu->chardev_m, &afu_master_attrs[i]);
385}