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