Commit | Line | Data |
---|---|---|
f204e0b8 IM |
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> | |
b087e619 | 13 | #include <linux/pci_regs.h> |
f204e0b8 IM |
14 | |
15 | #include "cxl.h" | |
16 | ||
17 | #define to_afu_chardev_m(d) dev_get_drvdata(d) | |
18 | ||
19 | /********* Adapter attributes **********************************************/ | |
20 | ||
21 | static ssize_t caia_version_show(struct device *device, | |
22 | struct device_attribute *attr, | |
23 | char *buf) | |
24 | { | |
25 | struct cxl *adapter = to_cxl_adapter(device); | |
26 | ||
27 | return scnprintf(buf, PAGE_SIZE, "%i.%i\n", adapter->caia_major, | |
28 | adapter->caia_minor); | |
29 | } | |
30 | ||
31 | static ssize_t psl_revision_show(struct device *device, | |
32 | struct device_attribute *attr, | |
33 | char *buf) | |
34 | { | |
35 | struct cxl *adapter = to_cxl_adapter(device); | |
36 | ||
37 | return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->psl_rev); | |
38 | } | |
39 | ||
40 | static ssize_t base_image_show(struct device *device, | |
41 | struct device_attribute *attr, | |
42 | char *buf) | |
43 | { | |
44 | struct cxl *adapter = to_cxl_adapter(device); | |
45 | ||
46 | return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->base_image); | |
47 | } | |
48 | ||
49 | static ssize_t image_loaded_show(struct device *device, | |
50 | struct device_attribute *attr, | |
51 | char *buf) | |
52 | { | |
53 | struct cxl *adapter = to_cxl_adapter(device); | |
54 | ||
55 | if (adapter->user_image_loaded) | |
56 | return scnprintf(buf, PAGE_SIZE, "user\n"); | |
57 | return scnprintf(buf, PAGE_SIZE, "factory\n"); | |
58 | } | |
59 | ||
e009a7e8 FB |
60 | static ssize_t psl_timebase_synced_show(struct device *device, |
61 | struct device_attribute *attr, | |
62 | char *buf) | |
63 | { | |
64 | struct cxl *adapter = to_cxl_adapter(device); | |
65 | ||
66 | return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->psl_timebase_synced); | |
67 | } | |
68 | ||
62fa19d4 RG |
69 | static ssize_t reset_adapter_store(struct device *device, |
70 | struct device_attribute *attr, | |
71 | const char *buf, size_t count) | |
72 | { | |
73 | struct cxl *adapter = to_cxl_adapter(device); | |
74 | int rc; | |
75 | int val; | |
76 | ||
77 | rc = sscanf(buf, "%i", &val); | |
78 | if ((rc != 1) || (val != 1)) | |
79 | return -EINVAL; | |
80 | ||
5be587b1 | 81 | if ((rc = cxl_ops->adapter_reset(adapter))) |
62fa19d4 RG |
82 | return rc; |
83 | return count; | |
84 | } | |
85 | ||
95bc11bc RG |
86 | static ssize_t load_image_on_perst_show(struct device *device, |
87 | struct device_attribute *attr, | |
88 | char *buf) | |
89 | { | |
90 | struct cxl *adapter = to_cxl_adapter(device); | |
91 | ||
92 | if (!adapter->perst_loads_image) | |
93 | return scnprintf(buf, PAGE_SIZE, "none\n"); | |
94 | ||
95 | if (adapter->perst_select_user) | |
96 | return scnprintf(buf, PAGE_SIZE, "user\n"); | |
97 | return scnprintf(buf, PAGE_SIZE, "factory\n"); | |
98 | } | |
99 | ||
100 | static ssize_t load_image_on_perst_store(struct device *device, | |
101 | struct device_attribute *attr, | |
102 | const char *buf, size_t count) | |
103 | { | |
104 | struct cxl *adapter = to_cxl_adapter(device); | |
105 | int rc; | |
106 | ||
107 | if (!strncmp(buf, "none", 4)) | |
108 | adapter->perst_loads_image = false; | |
109 | else if (!strncmp(buf, "user", 4)) { | |
110 | adapter->perst_select_user = true; | |
111 | adapter->perst_loads_image = true; | |
112 | } else if (!strncmp(buf, "factory", 7)) { | |
113 | adapter->perst_select_user = false; | |
114 | adapter->perst_loads_image = true; | |
115 | } else | |
116 | return -EINVAL; | |
117 | ||
118 | if ((rc = cxl_update_image_control(adapter))) | |
119 | return rc; | |
120 | ||
121 | return count; | |
122 | } | |
123 | ||
13e68d8b DA |
124 | static ssize_t perst_reloads_same_image_show(struct device *device, |
125 | struct device_attribute *attr, | |
126 | char *buf) | |
127 | { | |
128 | struct cxl *adapter = to_cxl_adapter(device); | |
129 | ||
130 | return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->perst_same_image); | |
131 | } | |
132 | ||
133 | static ssize_t perst_reloads_same_image_store(struct device *device, | |
134 | struct device_attribute *attr, | |
135 | const char *buf, size_t count) | |
136 | { | |
137 | struct cxl *adapter = to_cxl_adapter(device); | |
138 | int rc; | |
139 | int val; | |
140 | ||
141 | rc = sscanf(buf, "%i", &val); | |
142 | if ((rc != 1) || !(val == 1 || val == 0)) | |
143 | return -EINVAL; | |
144 | ||
145 | adapter->perst_same_image = (val == 1 ? true : false); | |
146 | return count; | |
147 | } | |
148 | ||
f204e0b8 IM |
149 | static struct device_attribute adapter_attrs[] = { |
150 | __ATTR_RO(caia_version), | |
151 | __ATTR_RO(psl_revision), | |
152 | __ATTR_RO(base_image), | |
153 | __ATTR_RO(image_loaded), | |
e009a7e8 | 154 | __ATTR_RO(psl_timebase_synced), |
95bc11bc | 155 | __ATTR_RW(load_image_on_perst), |
13e68d8b | 156 | __ATTR_RW(perst_reloads_same_image), |
62fa19d4 | 157 | __ATTR(reset, S_IWUSR, NULL, reset_adapter_store), |
f204e0b8 IM |
158 | }; |
159 | ||
160 | ||
161 | /********* AFU master specific attributes **********************************/ | |
162 | ||
163 | static ssize_t mmio_size_show_master(struct device *device, | |
164 | struct device_attribute *attr, | |
165 | char *buf) | |
166 | { | |
167 | struct cxl_afu *afu = to_afu_chardev_m(device); | |
168 | ||
169 | return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->adapter->ps_size); | |
170 | } | |
171 | ||
172 | static ssize_t pp_mmio_off_show(struct device *device, | |
173 | struct device_attribute *attr, | |
174 | char *buf) | |
175 | { | |
176 | struct cxl_afu *afu = to_afu_chardev_m(device); | |
177 | ||
cbffa3a5 | 178 | return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->native->pp_offset); |
f204e0b8 IM |
179 | } |
180 | ||
181 | static ssize_t pp_mmio_len_show(struct device *device, | |
182 | struct device_attribute *attr, | |
183 | char *buf) | |
184 | { | |
185 | struct cxl_afu *afu = to_afu_chardev_m(device); | |
186 | ||
187 | return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->pp_size); | |
188 | } | |
189 | ||
190 | static struct device_attribute afu_master_attrs[] = { | |
191 | __ATTR(mmio_size, S_IRUGO, mmio_size_show_master, NULL), | |
192 | __ATTR_RO(pp_mmio_off), | |
193 | __ATTR_RO(pp_mmio_len), | |
194 | }; | |
195 | ||
196 | ||
197 | /********* AFU attributes **************************************************/ | |
198 | ||
199 | static ssize_t mmio_size_show(struct device *device, | |
200 | struct device_attribute *attr, | |
201 | char *buf) | |
202 | { | |
203 | struct cxl_afu *afu = to_cxl_afu(device); | |
204 | ||
205 | if (afu->pp_size) | |
206 | return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->pp_size); | |
207 | return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->adapter->ps_size); | |
208 | } | |
209 | ||
210 | static ssize_t reset_store_afu(struct device *device, | |
211 | struct device_attribute *attr, | |
212 | const char *buf, size_t count) | |
213 | { | |
214 | struct cxl_afu *afu = to_cxl_afu(device); | |
215 | int rc; | |
216 | ||
217 | /* Not safe to reset if it is currently in use */ | |
ee41d11d | 218 | mutex_lock(&afu->contexts_lock); |
f204e0b8 IM |
219 | if (!idr_is_empty(&afu->contexts_idr)) { |
220 | rc = -EBUSY; | |
221 | goto err; | |
222 | } | |
223 | ||
5be587b1 | 224 | if ((rc = cxl_ops->afu_reset(afu))) |
f204e0b8 IM |
225 | goto err; |
226 | ||
227 | rc = count; | |
228 | err: | |
ee41d11d | 229 | mutex_unlock(&afu->contexts_lock); |
f204e0b8 IM |
230 | return rc; |
231 | } | |
232 | ||
233 | static ssize_t irqs_min_show(struct device *device, | |
234 | struct device_attribute *attr, | |
235 | char *buf) | |
236 | { | |
237 | struct cxl_afu *afu = to_cxl_afu(device); | |
238 | ||
239 | return scnprintf(buf, PAGE_SIZE, "%i\n", afu->pp_irqs); | |
240 | } | |
241 | ||
242 | static ssize_t irqs_max_show(struct device *device, | |
243 | struct device_attribute *attr, | |
244 | char *buf) | |
245 | { | |
246 | struct cxl_afu *afu = to_cxl_afu(device); | |
247 | ||
248 | return scnprintf(buf, PAGE_SIZE, "%i\n", afu->irqs_max); | |
249 | } | |
250 | ||
251 | static ssize_t irqs_max_store(struct device *device, | |
252 | struct device_attribute *attr, | |
253 | const char *buf, size_t count) | |
254 | { | |
255 | struct cxl_afu *afu = to_cxl_afu(device); | |
256 | ssize_t ret; | |
257 | int irqs_max; | |
258 | ||
259 | ret = sscanf(buf, "%i", &irqs_max); | |
260 | if (ret != 1) | |
261 | return -EINVAL; | |
262 | ||
263 | if (irqs_max < afu->pp_irqs) | |
264 | return -EINVAL; | |
265 | ||
4752876c CL |
266 | if (cpu_has_feature(CPU_FTR_HVMODE)) { |
267 | if (irqs_max > afu->adapter->user_irqs) | |
268 | return -EINVAL; | |
269 | } else { | |
270 | /* pHyp sets a per-AFU limit */ | |
271 | if (irqs_max > afu->guest->max_ints) | |
272 | return -EINVAL; | |
273 | } | |
f204e0b8 IM |
274 | |
275 | afu->irqs_max = irqs_max; | |
276 | return count; | |
277 | } | |
278 | ||
279 | static ssize_t modes_supported_show(struct device *device, | |
280 | struct device_attribute *attr, char *buf) | |
281 | { | |
282 | struct cxl_afu *afu = to_cxl_afu(device); | |
283 | char *p = buf, *end = buf + PAGE_SIZE; | |
284 | ||
285 | if (afu->modes_supported & CXL_MODE_DEDICATED) | |
286 | p += scnprintf(p, end - p, "dedicated_process\n"); | |
287 | if (afu->modes_supported & CXL_MODE_DIRECTED) | |
288 | p += scnprintf(p, end - p, "afu_directed\n"); | |
289 | return (p - buf); | |
290 | } | |
291 | ||
292 | static ssize_t prefault_mode_show(struct device *device, | |
293 | struct device_attribute *attr, | |
294 | char *buf) | |
295 | { | |
296 | struct cxl_afu *afu = to_cxl_afu(device); | |
297 | ||
298 | switch (afu->prefault_mode) { | |
299 | case CXL_PREFAULT_WED: | |
300 | return scnprintf(buf, PAGE_SIZE, "work_element_descriptor\n"); | |
301 | case CXL_PREFAULT_ALL: | |
302 | return scnprintf(buf, PAGE_SIZE, "all\n"); | |
303 | default: | |
304 | return scnprintf(buf, PAGE_SIZE, "none\n"); | |
305 | } | |
306 | } | |
307 | ||
308 | static ssize_t prefault_mode_store(struct device *device, | |
309 | struct device_attribute *attr, | |
310 | const char *buf, size_t count) | |
311 | { | |
312 | struct cxl_afu *afu = to_cxl_afu(device); | |
313 | enum prefault_modes mode = -1; | |
314 | ||
315 | if (!strncmp(buf, "work_element_descriptor", 23)) | |
316 | mode = CXL_PREFAULT_WED; | |
317 | if (!strncmp(buf, "all", 3)) | |
318 | mode = CXL_PREFAULT_ALL; | |
319 | if (!strncmp(buf, "none", 4)) | |
320 | mode = CXL_PREFAULT_NONE; | |
321 | ||
322 | if (mode == -1) | |
323 | return -EINVAL; | |
324 | ||
325 | afu->prefault_mode = mode; | |
326 | return count; | |
327 | } | |
328 | ||
329 | static ssize_t mode_show(struct device *device, | |
330 | struct device_attribute *attr, | |
331 | char *buf) | |
332 | { | |
333 | struct cxl_afu *afu = to_cxl_afu(device); | |
334 | ||
335 | if (afu->current_mode == CXL_MODE_DEDICATED) | |
336 | return scnprintf(buf, PAGE_SIZE, "dedicated_process\n"); | |
337 | if (afu->current_mode == CXL_MODE_DIRECTED) | |
338 | return scnprintf(buf, PAGE_SIZE, "afu_directed\n"); | |
339 | return scnprintf(buf, PAGE_SIZE, "none\n"); | |
340 | } | |
341 | ||
342 | static ssize_t mode_store(struct device *device, struct device_attribute *attr, | |
343 | const char *buf, size_t count) | |
344 | { | |
345 | struct cxl_afu *afu = to_cxl_afu(device); | |
346 | int old_mode, mode = -1; | |
347 | int rc = -EBUSY; | |
348 | ||
349 | /* can't change this if we have a user */ | |
ee41d11d | 350 | mutex_lock(&afu->contexts_lock); |
f204e0b8 IM |
351 | if (!idr_is_empty(&afu->contexts_idr)) |
352 | goto err; | |
353 | ||
354 | if (!strncmp(buf, "dedicated_process", 17)) | |
355 | mode = CXL_MODE_DEDICATED; | |
356 | if (!strncmp(buf, "afu_directed", 12)) | |
357 | mode = CXL_MODE_DIRECTED; | |
358 | if (!strncmp(buf, "none", 4)) | |
359 | mode = 0; | |
360 | ||
361 | if (mode == -1) { | |
362 | rc = -EINVAL; | |
363 | goto err; | |
364 | } | |
365 | ||
366 | /* | |
5be587b1 | 367 | * afu_deactivate_mode needs to be done outside the lock, prevent |
f204e0b8 IM |
368 | * other contexts coming in before we are ready: |
369 | */ | |
370 | old_mode = afu->current_mode; | |
371 | afu->current_mode = 0; | |
372 | afu->num_procs = 0; | |
373 | ||
ee41d11d | 374 | mutex_unlock(&afu->contexts_lock); |
f204e0b8 | 375 | |
5be587b1 | 376 | if ((rc = cxl_ops->afu_deactivate_mode(afu, old_mode))) |
f204e0b8 | 377 | return rc; |
5be587b1 | 378 | if ((rc = cxl_ops->afu_activate_mode(afu, mode))) |
f204e0b8 IM |
379 | return rc; |
380 | ||
381 | return count; | |
382 | err: | |
ee41d11d | 383 | mutex_unlock(&afu->contexts_lock); |
f204e0b8 IM |
384 | return rc; |
385 | } | |
386 | ||
387 | static ssize_t api_version_show(struct device *device, | |
388 | struct device_attribute *attr, | |
389 | char *buf) | |
390 | { | |
391 | return scnprintf(buf, PAGE_SIZE, "%i\n", CXL_API_VERSION); | |
392 | } | |
393 | ||
394 | static ssize_t api_version_compatible_show(struct device *device, | |
395 | struct device_attribute *attr, | |
396 | char *buf) | |
397 | { | |
398 | return scnprintf(buf, PAGE_SIZE, "%i\n", CXL_API_VERSION_COMPATIBLE); | |
399 | } | |
400 | ||
e36f6fe1 VJ |
401 | static ssize_t afu_eb_read(struct file *filp, struct kobject *kobj, |
402 | struct bin_attribute *bin_attr, char *buf, | |
403 | loff_t off, size_t count) | |
404 | { | |
85016ff3 | 405 | struct cxl_afu *afu = to_cxl_afu(kobj_to_dev(kobj)); |
e36f6fe1 | 406 | |
5be587b1 | 407 | return cxl_ops->afu_read_err_buffer(afu, buf, off, count); |
e36f6fe1 VJ |
408 | } |
409 | ||
f204e0b8 IM |
410 | static struct device_attribute afu_attrs[] = { |
411 | __ATTR_RO(mmio_size), | |
412 | __ATTR_RO(irqs_min), | |
413 | __ATTR_RW(irqs_max), | |
414 | __ATTR_RO(modes_supported), | |
415 | __ATTR_RW(mode), | |
416 | __ATTR_RW(prefault_mode), | |
417 | __ATTR_RO(api_version), | |
418 | __ATTR_RO(api_version_compatible), | |
419 | __ATTR(reset, S_IWUSR, NULL, reset_store_afu), | |
420 | }; | |
421 | ||
f204e0b8 IM |
422 | int cxl_sysfs_adapter_add(struct cxl *adapter) |
423 | { | |
4752876c | 424 | struct device_attribute *dev_attr; |
f204e0b8 IM |
425 | int i, rc; |
426 | ||
427 | for (i = 0; i < ARRAY_SIZE(adapter_attrs); i++) { | |
4752876c CL |
428 | dev_attr = &adapter_attrs[i]; |
429 | if (cxl_ops->support_attributes(dev_attr->attr.name, | |
430 | CXL_ADAPTER_ATTRS)) { | |
431 | if ((rc = device_create_file(&adapter->dev, dev_attr))) | |
432 | goto err; | |
433 | } | |
f204e0b8 IM |
434 | } |
435 | return 0; | |
436 | err: | |
4752876c CL |
437 | for (i--; i >= 0; i--) { |
438 | dev_attr = &adapter_attrs[i]; | |
439 | if (cxl_ops->support_attributes(dev_attr->attr.name, | |
440 | CXL_ADAPTER_ATTRS)) | |
441 | device_remove_file(&adapter->dev, dev_attr); | |
442 | } | |
f204e0b8 IM |
443 | return rc; |
444 | } | |
4752876c | 445 | |
f204e0b8 IM |
446 | void cxl_sysfs_adapter_remove(struct cxl *adapter) |
447 | { | |
4752876c | 448 | struct device_attribute *dev_attr; |
f204e0b8 IM |
449 | int i; |
450 | ||
4752876c CL |
451 | for (i = 0; i < ARRAY_SIZE(adapter_attrs); i++) { |
452 | dev_attr = &adapter_attrs[i]; | |
453 | if (cxl_ops->support_attributes(dev_attr->attr.name, | |
454 | CXL_ADAPTER_ATTRS)) | |
455 | device_remove_file(&adapter->dev, dev_attr); | |
456 | } | |
f204e0b8 IM |
457 | } |
458 | ||
b087e619 IM |
459 | struct afu_config_record { |
460 | struct kobject kobj; | |
461 | struct bin_attribute config_attr; | |
462 | struct list_head list; | |
463 | int cr; | |
464 | u16 device; | |
465 | u16 vendor; | |
466 | u32 class; | |
467 | }; | |
468 | ||
469 | #define to_cr(obj) container_of(obj, struct afu_config_record, kobj) | |
470 | ||
471 | static ssize_t vendor_show(struct kobject *kobj, | |
472 | struct kobj_attribute *attr, char *buf) | |
473 | { | |
474 | struct afu_config_record *cr = to_cr(kobj); | |
475 | ||
476 | return scnprintf(buf, PAGE_SIZE, "0x%.4x\n", cr->vendor); | |
477 | } | |
478 | ||
479 | static ssize_t device_show(struct kobject *kobj, | |
480 | struct kobj_attribute *attr, char *buf) | |
481 | { | |
482 | struct afu_config_record *cr = to_cr(kobj); | |
483 | ||
484 | return scnprintf(buf, PAGE_SIZE, "0x%.4x\n", cr->device); | |
485 | } | |
486 | ||
487 | static ssize_t class_show(struct kobject *kobj, | |
488 | struct kobj_attribute *attr, char *buf) | |
489 | { | |
490 | struct afu_config_record *cr = to_cr(kobj); | |
491 | ||
492 | return scnprintf(buf, PAGE_SIZE, "0x%.6x\n", cr->class); | |
493 | } | |
494 | ||
495 | static ssize_t afu_read_config(struct file *filp, struct kobject *kobj, | |
496 | struct bin_attribute *bin_attr, char *buf, | |
497 | loff_t off, size_t count) | |
498 | { | |
499 | struct afu_config_record *cr = to_cr(kobj); | |
85016ff3 | 500 | struct cxl_afu *afu = to_cxl_afu(kobj_to_dev(kobj->parent)); |
b087e619 | 501 | |
5be587b1 | 502 | u64 i, j, val, rc; |
b087e619 IM |
503 | |
504 | for (i = 0; i < count;) { | |
5be587b1 FB |
505 | rc = cxl_ops->afu_cr_read64(afu, cr->cr, off & ~0x7, &val); |
506 | if (rc) | |
507 | val = ~0ULL; | |
b087e619 IM |
508 | for (j = off & 0x7; j < 8 && i < count; i++, j++, off++) |
509 | buf[i] = (val >> (j * 8)) & 0xff; | |
510 | } | |
511 | ||
512 | return count; | |
513 | } | |
514 | ||
515 | static struct kobj_attribute vendor_attribute = | |
516 | __ATTR_RO(vendor); | |
517 | static struct kobj_attribute device_attribute = | |
518 | __ATTR_RO(device); | |
519 | static struct kobj_attribute class_attribute = | |
520 | __ATTR_RO(class); | |
521 | ||
522 | static struct attribute *afu_cr_attrs[] = { | |
523 | &vendor_attribute.attr, | |
524 | &device_attribute.attr, | |
525 | &class_attribute.attr, | |
526 | NULL, | |
527 | }; | |
528 | ||
529 | static void release_afu_config_record(struct kobject *kobj) | |
530 | { | |
531 | struct afu_config_record *cr = to_cr(kobj); | |
532 | ||
533 | kfree(cr); | |
534 | } | |
535 | ||
536 | static struct kobj_type afu_config_record_type = { | |
537 | .sysfs_ops = &kobj_sysfs_ops, | |
538 | .release = release_afu_config_record, | |
539 | .default_attrs = afu_cr_attrs, | |
540 | }; | |
541 | ||
542 | static struct afu_config_record *cxl_sysfs_afu_new_cr(struct cxl_afu *afu, int cr_idx) | |
543 | { | |
544 | struct afu_config_record *cr; | |
545 | int rc; | |
546 | ||
547 | cr = kzalloc(sizeof(struct afu_config_record), GFP_KERNEL); | |
548 | if (!cr) | |
549 | return ERR_PTR(-ENOMEM); | |
550 | ||
551 | cr->cr = cr_idx; | |
5be587b1 FB |
552 | |
553 | rc = cxl_ops->afu_cr_read16(afu, cr_idx, PCI_DEVICE_ID, &cr->device); | |
554 | if (rc) | |
555 | goto err; | |
556 | rc = cxl_ops->afu_cr_read16(afu, cr_idx, PCI_VENDOR_ID, &cr->vendor); | |
557 | if (rc) | |
558 | goto err; | |
559 | rc = cxl_ops->afu_cr_read32(afu, cr_idx, PCI_CLASS_REVISION, &cr->class); | |
560 | if (rc) | |
561 | goto err; | |
562 | cr->class >>= 8; | |
b087e619 IM |
563 | |
564 | /* | |
565 | * Export raw AFU PCIe like config record. For now this is read only by | |
566 | * root - we can expand that later to be readable by non-root and maybe | |
4752876c | 567 | * even writable provided we have a good use-case. Once we support |
b087e619 IM |
568 | * exposing AFUs through a virtual PHB they will get that for free from |
569 | * Linux' PCI infrastructure, but until then it's not clear that we | |
570 | * need it for anything since the main use case is just identifying | |
571 | * AFUs, which can be done via the vendor, device and class attributes. | |
572 | */ | |
573 | sysfs_bin_attr_init(&cr->config_attr); | |
574 | cr->config_attr.attr.name = "config"; | |
575 | cr->config_attr.attr.mode = S_IRUSR; | |
576 | cr->config_attr.size = afu->crs_len; | |
577 | cr->config_attr.read = afu_read_config; | |
578 | ||
579 | rc = kobject_init_and_add(&cr->kobj, &afu_config_record_type, | |
580 | &afu->dev.kobj, "cr%i", cr->cr); | |
581 | if (rc) | |
582 | goto err; | |
583 | ||
584 | rc = sysfs_create_bin_file(&cr->kobj, &cr->config_attr); | |
585 | if (rc) | |
586 | goto err1; | |
587 | ||
588 | rc = kobject_uevent(&cr->kobj, KOBJ_ADD); | |
589 | if (rc) | |
590 | goto err2; | |
591 | ||
592 | return cr; | |
593 | err2: | |
594 | sysfs_remove_bin_file(&cr->kobj, &cr->config_attr); | |
595 | err1: | |
596 | kobject_put(&cr->kobj); | |
597 | return ERR_PTR(rc); | |
598 | err: | |
599 | kfree(cr); | |
600 | return ERR_PTR(rc); | |
601 | } | |
602 | ||
603 | void cxl_sysfs_afu_remove(struct cxl_afu *afu) | |
604 | { | |
4752876c | 605 | struct device_attribute *dev_attr; |
b087e619 IM |
606 | struct afu_config_record *cr, *tmp; |
607 | int i; | |
608 | ||
e36f6fe1 VJ |
609 | /* remove the err buffer bin attribute */ |
610 | if (afu->eb_len) | |
611 | device_remove_bin_file(&afu->dev, &afu->attr_eb); | |
612 | ||
4752876c CL |
613 | for (i = 0; i < ARRAY_SIZE(afu_attrs); i++) { |
614 | dev_attr = &afu_attrs[i]; | |
615 | if (cxl_ops->support_attributes(dev_attr->attr.name, | |
616 | CXL_AFU_ATTRS)) | |
617 | device_remove_file(&afu->dev, &afu_attrs[i]); | |
618 | } | |
b087e619 IM |
619 | |
620 | list_for_each_entry_safe(cr, tmp, &afu->crs, list) { | |
621 | sysfs_remove_bin_file(&cr->kobj, &cr->config_attr); | |
622 | kobject_put(&cr->kobj); | |
623 | } | |
624 | } | |
625 | ||
f204e0b8 IM |
626 | int cxl_sysfs_afu_add(struct cxl_afu *afu) |
627 | { | |
4752876c | 628 | struct device_attribute *dev_attr; |
b087e619 | 629 | struct afu_config_record *cr; |
f204e0b8 IM |
630 | int i, rc; |
631 | ||
b087e619 IM |
632 | INIT_LIST_HEAD(&afu->crs); |
633 | ||
f204e0b8 | 634 | for (i = 0; i < ARRAY_SIZE(afu_attrs); i++) { |
4752876c CL |
635 | dev_attr = &afu_attrs[i]; |
636 | if (cxl_ops->support_attributes(dev_attr->attr.name, | |
637 | CXL_AFU_ATTRS)) { | |
638 | if ((rc = device_create_file(&afu->dev, &afu_attrs[i]))) | |
639 | goto err; | |
640 | } | |
f204e0b8 IM |
641 | } |
642 | ||
e36f6fe1 VJ |
643 | /* conditionally create the add the binary file for error info buffer */ |
644 | if (afu->eb_len) { | |
d6eb71a6 VJ |
645 | sysfs_attr_init(&afu->attr_eb.attr); |
646 | ||
e36f6fe1 VJ |
647 | afu->attr_eb.attr.name = "afu_err_buff"; |
648 | afu->attr_eb.attr.mode = S_IRUGO; | |
649 | afu->attr_eb.size = afu->eb_len; | |
650 | afu->attr_eb.read = afu_eb_read; | |
651 | ||
652 | rc = device_create_bin_file(&afu->dev, &afu->attr_eb); | |
653 | if (rc) { | |
654 | dev_err(&afu->dev, | |
655 | "Unable to create eb attr for the afu. Err(%d)\n", | |
656 | rc); | |
657 | goto err; | |
658 | } | |
659 | } | |
660 | ||
b087e619 IM |
661 | for (i = 0; i < afu->crs_num; i++) { |
662 | cr = cxl_sysfs_afu_new_cr(afu, i); | |
663 | if (IS_ERR(cr)) { | |
664 | rc = PTR_ERR(cr); | |
665 | goto err1; | |
666 | } | |
667 | list_add(&cr->list, &afu->crs); | |
668 | } | |
669 | ||
f204e0b8 IM |
670 | return 0; |
671 | ||
b087e619 IM |
672 | err1: |
673 | cxl_sysfs_afu_remove(afu); | |
674 | return rc; | |
f204e0b8 | 675 | err: |
e36f6fe1 VJ |
676 | /* reset the eb_len as we havent created the bin attr */ |
677 | afu->eb_len = 0; | |
678 | ||
4752876c CL |
679 | for (i--; i >= 0; i--) { |
680 | dev_attr = &afu_attrs[i]; | |
681 | if (cxl_ops->support_attributes(dev_attr->attr.name, | |
682 | CXL_AFU_ATTRS)) | |
f204e0b8 | 683 | device_remove_file(&afu->dev, &afu_attrs[i]); |
4752876c | 684 | } |
f204e0b8 IM |
685 | return rc; |
686 | } | |
687 | ||
f204e0b8 IM |
688 | int cxl_sysfs_afu_m_add(struct cxl_afu *afu) |
689 | { | |
4752876c | 690 | struct device_attribute *dev_attr; |
f204e0b8 IM |
691 | int i, rc; |
692 | ||
693 | for (i = 0; i < ARRAY_SIZE(afu_master_attrs); i++) { | |
4752876c CL |
694 | dev_attr = &afu_master_attrs[i]; |
695 | if (cxl_ops->support_attributes(dev_attr->attr.name, | |
696 | CXL_AFU_MASTER_ATTRS)) { | |
697 | if ((rc = device_create_file(afu->chardev_m, &afu_master_attrs[i]))) | |
698 | goto err; | |
699 | } | |
f204e0b8 IM |
700 | } |
701 | ||
702 | return 0; | |
703 | ||
704 | err: | |
4752876c CL |
705 | for (i--; i >= 0; i--) { |
706 | dev_attr = &afu_master_attrs[i]; | |
707 | if (cxl_ops->support_attributes(dev_attr->attr.name, | |
708 | CXL_AFU_MASTER_ATTRS)) | |
709 | device_remove_file(afu->chardev_m, &afu_master_attrs[i]); | |
710 | } | |
f204e0b8 IM |
711 | return rc; |
712 | } | |
713 | ||
714 | void cxl_sysfs_afu_m_remove(struct cxl_afu *afu) | |
715 | { | |
4752876c | 716 | struct device_attribute *dev_attr; |
f204e0b8 IM |
717 | int i; |
718 | ||
4752876c CL |
719 | for (i = 0; i < ARRAY_SIZE(afu_master_attrs); i++) { |
720 | dev_attr = &afu_master_attrs[i]; | |
721 | if (cxl_ops->support_attributes(dev_attr->attr.name, | |
722 | CXL_AFU_MASTER_ATTRS)) | |
723 | device_remove_file(afu->chardev_m, &afu_master_attrs[i]); | |
724 | } | |
f204e0b8 | 725 | } |