1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * configfs to configure the PCI endpoint |
4 | * |
5 | * Copyright (C) 2017 Texas Instruments |
6 | * Author: Kishon Vijay Abraham I <kishon@ti.com> |
7 | */ |
8 | |
9 | #include <linux/module.h> |
10 | #include <linux/idr.h> |
11 | #include <linux/slab.h> |
12 | |
13 | #include <linux/pci-epc.h> |
14 | #include <linux/pci-epf.h> |
15 | #include <linux/pci-ep-cfs.h> |
16 | |
17 | static DEFINE_IDR(functions_idr); |
18 | static DEFINE_MUTEX(functions_mutex); |
19 | static struct config_group *functions_group; |
20 | static struct config_group *controllers_group; |
21 | |
22 | struct pci_epf_group { |
23 | struct config_group group; |
24 | struct config_group primary_epc_group; |
25 | struct config_group secondary_epc_group; |
26 | struct config_group *type_group; |
27 | struct delayed_work cfs_work; |
28 | struct pci_epf *epf; |
29 | int index; |
30 | }; |
31 | |
32 | struct pci_epc_group { |
33 | struct config_group group; |
34 | struct pci_epc *epc; |
35 | bool start; |
36 | }; |
37 | |
38 | static inline struct pci_epf_group *to_pci_epf_group(struct config_item *item) |
39 | { |
40 | return container_of(to_config_group(item), struct pci_epf_group, group); |
41 | } |
42 | |
43 | static inline struct pci_epc_group *to_pci_epc_group(struct config_item *item) |
44 | { |
45 | return container_of(to_config_group(item), struct pci_epc_group, group); |
46 | } |
47 | |
48 | static int pci_secondary_epc_epf_link(struct config_item *epf_item, |
49 | struct config_item *epc_item) |
50 | { |
51 | int ret; |
52 | struct pci_epf_group *epf_group = to_pci_epf_group(item: epf_item->ci_parent); |
53 | struct pci_epc_group *epc_group = to_pci_epc_group(item: epc_item); |
54 | struct pci_epc *epc = epc_group->epc; |
55 | struct pci_epf *epf = epf_group->epf; |
56 | |
57 | ret = pci_epc_add_epf(epc, epf, type: SECONDARY_INTERFACE); |
58 | if (ret) |
59 | return ret; |
60 | |
61 | ret = pci_epf_bind(epf); |
62 | if (ret) { |
63 | pci_epc_remove_epf(epc, epf, type: SECONDARY_INTERFACE); |
64 | return ret; |
65 | } |
66 | |
67 | return 0; |
68 | } |
69 | |
70 | static void pci_secondary_epc_epf_unlink(struct config_item *epc_item, |
71 | struct config_item *epf_item) |
72 | { |
73 | struct pci_epf_group *epf_group = to_pci_epf_group(item: epf_item->ci_parent); |
74 | struct pci_epc_group *epc_group = to_pci_epc_group(item: epc_item); |
75 | struct pci_epc *epc; |
76 | struct pci_epf *epf; |
77 | |
78 | WARN_ON_ONCE(epc_group->start); |
79 | |
80 | epc = epc_group->epc; |
81 | epf = epf_group->epf; |
82 | pci_epf_unbind(epf); |
83 | pci_epc_remove_epf(epc, epf, type: SECONDARY_INTERFACE); |
84 | } |
85 | |
86 | static struct configfs_item_operations pci_secondary_epc_item_ops = { |
87 | .allow_link = pci_secondary_epc_epf_link, |
88 | .drop_link = pci_secondary_epc_epf_unlink, |
89 | }; |
90 | |
91 | static const struct config_item_type pci_secondary_epc_type = { |
92 | .ct_item_ops = &pci_secondary_epc_item_ops, |
93 | .ct_owner = THIS_MODULE, |
94 | }; |
95 | |
96 | static struct config_group |
97 | *pci_ep_cfs_add_secondary_group(struct pci_epf_group *epf_group) |
98 | { |
99 | struct config_group *secondary_epc_group; |
100 | |
101 | secondary_epc_group = &epf_group->secondary_epc_group; |
102 | config_group_init_type_name(group: secondary_epc_group, name: "secondary" , |
103 | type: &pci_secondary_epc_type); |
104 | configfs_register_group(parent_group: &epf_group->group, group: secondary_epc_group); |
105 | |
106 | return secondary_epc_group; |
107 | } |
108 | |
109 | static int pci_primary_epc_epf_link(struct config_item *epf_item, |
110 | struct config_item *epc_item) |
111 | { |
112 | int ret; |
113 | struct pci_epf_group *epf_group = to_pci_epf_group(item: epf_item->ci_parent); |
114 | struct pci_epc_group *epc_group = to_pci_epc_group(item: epc_item); |
115 | struct pci_epc *epc = epc_group->epc; |
116 | struct pci_epf *epf = epf_group->epf; |
117 | |
118 | ret = pci_epc_add_epf(epc, epf, type: PRIMARY_INTERFACE); |
119 | if (ret) |
120 | return ret; |
121 | |
122 | ret = pci_epf_bind(epf); |
123 | if (ret) { |
124 | pci_epc_remove_epf(epc, epf, type: PRIMARY_INTERFACE); |
125 | return ret; |
126 | } |
127 | |
128 | return 0; |
129 | } |
130 | |
131 | static void pci_primary_epc_epf_unlink(struct config_item *epc_item, |
132 | struct config_item *epf_item) |
133 | { |
134 | struct pci_epf_group *epf_group = to_pci_epf_group(item: epf_item->ci_parent); |
135 | struct pci_epc_group *epc_group = to_pci_epc_group(item: epc_item); |
136 | struct pci_epc *epc; |
137 | struct pci_epf *epf; |
138 | |
139 | WARN_ON_ONCE(epc_group->start); |
140 | |
141 | epc = epc_group->epc; |
142 | epf = epf_group->epf; |
143 | pci_epf_unbind(epf); |
144 | pci_epc_remove_epf(epc, epf, type: PRIMARY_INTERFACE); |
145 | } |
146 | |
147 | static struct configfs_item_operations pci_primary_epc_item_ops = { |
148 | .allow_link = pci_primary_epc_epf_link, |
149 | .drop_link = pci_primary_epc_epf_unlink, |
150 | }; |
151 | |
152 | static const struct config_item_type pci_primary_epc_type = { |
153 | .ct_item_ops = &pci_primary_epc_item_ops, |
154 | .ct_owner = THIS_MODULE, |
155 | }; |
156 | |
157 | static struct config_group |
158 | *pci_ep_cfs_add_primary_group(struct pci_epf_group *epf_group) |
159 | { |
160 | struct config_group *primary_epc_group = &epf_group->primary_epc_group; |
161 | |
162 | config_group_init_type_name(group: primary_epc_group, name: "primary" , |
163 | type: &pci_primary_epc_type); |
164 | configfs_register_group(parent_group: &epf_group->group, group: primary_epc_group); |
165 | |
166 | return primary_epc_group; |
167 | } |
168 | |
169 | static ssize_t pci_epc_start_store(struct config_item *item, const char *page, |
170 | size_t len) |
171 | { |
172 | int ret; |
173 | bool start; |
174 | struct pci_epc *epc; |
175 | struct pci_epc_group *epc_group = to_pci_epc_group(item); |
176 | |
177 | epc = epc_group->epc; |
178 | |
179 | if (kstrtobool(s: page, res: &start) < 0) |
180 | return -EINVAL; |
181 | |
182 | if (start == epc_group->start) |
183 | return -EALREADY; |
184 | |
185 | if (!start) { |
186 | pci_epc_stop(epc); |
187 | epc_group->start = 0; |
188 | return len; |
189 | } |
190 | |
191 | ret = pci_epc_start(epc); |
192 | if (ret) { |
193 | dev_err(&epc->dev, "failed to start endpoint controller\n" ); |
194 | return -EINVAL; |
195 | } |
196 | |
197 | epc_group->start = start; |
198 | |
199 | return len; |
200 | } |
201 | |
202 | static ssize_t pci_epc_start_show(struct config_item *item, char *page) |
203 | { |
204 | return sysfs_emit(buf: page, fmt: "%d\n" , to_pci_epc_group(item)->start); |
205 | } |
206 | |
207 | CONFIGFS_ATTR(pci_epc_, start); |
208 | |
209 | static struct configfs_attribute *pci_epc_attrs[] = { |
210 | &pci_epc_attr_start, |
211 | NULL, |
212 | }; |
213 | |
214 | static int pci_epc_epf_link(struct config_item *epc_item, |
215 | struct config_item *epf_item) |
216 | { |
217 | int ret; |
218 | struct pci_epf_group *epf_group = to_pci_epf_group(item: epf_item); |
219 | struct pci_epc_group *epc_group = to_pci_epc_group(item: epc_item); |
220 | struct pci_epc *epc = epc_group->epc; |
221 | struct pci_epf *epf = epf_group->epf; |
222 | |
223 | ret = pci_epc_add_epf(epc, epf, type: PRIMARY_INTERFACE); |
224 | if (ret) |
225 | return ret; |
226 | |
227 | ret = pci_epf_bind(epf); |
228 | if (ret) { |
229 | pci_epc_remove_epf(epc, epf, type: PRIMARY_INTERFACE); |
230 | return ret; |
231 | } |
232 | |
233 | return 0; |
234 | } |
235 | |
236 | static void pci_epc_epf_unlink(struct config_item *epc_item, |
237 | struct config_item *epf_item) |
238 | { |
239 | struct pci_epc *epc; |
240 | struct pci_epf *epf; |
241 | struct pci_epf_group *epf_group = to_pci_epf_group(item: epf_item); |
242 | struct pci_epc_group *epc_group = to_pci_epc_group(item: epc_item); |
243 | |
244 | WARN_ON_ONCE(epc_group->start); |
245 | |
246 | epc = epc_group->epc; |
247 | epf = epf_group->epf; |
248 | pci_epf_unbind(epf); |
249 | pci_epc_remove_epf(epc, epf, type: PRIMARY_INTERFACE); |
250 | } |
251 | |
252 | static struct configfs_item_operations pci_epc_item_ops = { |
253 | .allow_link = pci_epc_epf_link, |
254 | .drop_link = pci_epc_epf_unlink, |
255 | }; |
256 | |
257 | static const struct config_item_type pci_epc_type = { |
258 | .ct_item_ops = &pci_epc_item_ops, |
259 | .ct_attrs = pci_epc_attrs, |
260 | .ct_owner = THIS_MODULE, |
261 | }; |
262 | |
263 | struct config_group *pci_ep_cfs_add_epc_group(const char *name) |
264 | { |
265 | int ret; |
266 | struct pci_epc *epc; |
267 | struct config_group *group; |
268 | struct pci_epc_group *epc_group; |
269 | |
270 | epc_group = kzalloc(size: sizeof(*epc_group), GFP_KERNEL); |
271 | if (!epc_group) { |
272 | ret = -ENOMEM; |
273 | goto err; |
274 | } |
275 | |
276 | group = &epc_group->group; |
277 | |
278 | config_group_init_type_name(group, name, type: &pci_epc_type); |
279 | ret = configfs_register_group(parent_group: controllers_group, group); |
280 | if (ret) { |
281 | pr_err("failed to register configfs group for %s\n" , name); |
282 | goto err_register_group; |
283 | } |
284 | |
285 | epc = pci_epc_get(epc_name: name); |
286 | if (IS_ERR(ptr: epc)) { |
287 | ret = PTR_ERR(ptr: epc); |
288 | goto err_epc_get; |
289 | } |
290 | |
291 | epc_group->epc = epc; |
292 | |
293 | return group; |
294 | |
295 | err_epc_get: |
296 | configfs_unregister_group(group); |
297 | |
298 | err_register_group: |
299 | kfree(objp: epc_group); |
300 | |
301 | err: |
302 | return ERR_PTR(error: ret); |
303 | } |
304 | EXPORT_SYMBOL(pci_ep_cfs_add_epc_group); |
305 | |
306 | void pci_ep_cfs_remove_epc_group(struct config_group *group) |
307 | { |
308 | struct pci_epc_group *epc_group; |
309 | |
310 | if (!group) |
311 | return; |
312 | |
313 | epc_group = container_of(group, struct pci_epc_group, group); |
314 | pci_epc_put(epc: epc_group->epc); |
315 | configfs_unregister_group(group: &epc_group->group); |
316 | kfree(objp: epc_group); |
317 | } |
318 | EXPORT_SYMBOL(pci_ep_cfs_remove_epc_group); |
319 | |
320 | #define (_name) \ |
321 | static ssize_t pci_epf_##_name##_show(struct config_item *item, char *page) \ |
322 | { \ |
323 | struct pci_epf *epf = to_pci_epf_group(item)->epf; \ |
324 | if (WARN_ON_ONCE(!epf->header)) \ |
325 | return -EINVAL; \ |
326 | return sysfs_emit(page, "0x%04x\n", epf->header->_name); \ |
327 | } |
328 | |
329 | #define (_name) \ |
330 | static ssize_t pci_epf_##_name##_store(struct config_item *item, \ |
331 | const char *page, size_t len) \ |
332 | { \ |
333 | u32 val; \ |
334 | struct pci_epf *epf = to_pci_epf_group(item)->epf; \ |
335 | if (WARN_ON_ONCE(!epf->header)) \ |
336 | return -EINVAL; \ |
337 | if (kstrtou32(page, 0, &val) < 0) \ |
338 | return -EINVAL; \ |
339 | epf->header->_name = val; \ |
340 | return len; \ |
341 | } |
342 | |
343 | #define (_name) \ |
344 | static ssize_t pci_epf_##_name##_store(struct config_item *item, \ |
345 | const char *page, size_t len) \ |
346 | { \ |
347 | u16 val; \ |
348 | struct pci_epf *epf = to_pci_epf_group(item)->epf; \ |
349 | if (WARN_ON_ONCE(!epf->header)) \ |
350 | return -EINVAL; \ |
351 | if (kstrtou16(page, 0, &val) < 0) \ |
352 | return -EINVAL; \ |
353 | epf->header->_name = val; \ |
354 | return len; \ |
355 | } |
356 | |
357 | #define (_name) \ |
358 | static ssize_t pci_epf_##_name##_store(struct config_item *item, \ |
359 | const char *page, size_t len) \ |
360 | { \ |
361 | u8 val; \ |
362 | struct pci_epf *epf = to_pci_epf_group(item)->epf; \ |
363 | if (WARN_ON_ONCE(!epf->header)) \ |
364 | return -EINVAL; \ |
365 | if (kstrtou8(page, 0, &val) < 0) \ |
366 | return -EINVAL; \ |
367 | epf->header->_name = val; \ |
368 | return len; \ |
369 | } |
370 | |
371 | static ssize_t pci_epf_msi_interrupts_store(struct config_item *item, |
372 | const char *page, size_t len) |
373 | { |
374 | u8 val; |
375 | |
376 | if (kstrtou8(s: page, base: 0, res: &val) < 0) |
377 | return -EINVAL; |
378 | |
379 | to_pci_epf_group(item)->epf->msi_interrupts = val; |
380 | |
381 | return len; |
382 | } |
383 | |
384 | static ssize_t pci_epf_msi_interrupts_show(struct config_item *item, |
385 | char *page) |
386 | { |
387 | return sysfs_emit(buf: page, fmt: "%d\n" , |
388 | to_pci_epf_group(item)->epf->msi_interrupts); |
389 | } |
390 | |
391 | static ssize_t pci_epf_msix_interrupts_store(struct config_item *item, |
392 | const char *page, size_t len) |
393 | { |
394 | u16 val; |
395 | |
396 | if (kstrtou16(s: page, base: 0, res: &val) < 0) |
397 | return -EINVAL; |
398 | |
399 | to_pci_epf_group(item)->epf->msix_interrupts = val; |
400 | |
401 | return len; |
402 | } |
403 | |
404 | static ssize_t pci_epf_msix_interrupts_show(struct config_item *item, |
405 | char *page) |
406 | { |
407 | return sysfs_emit(buf: page, fmt: "%d\n" , |
408 | to_pci_epf_group(item)->epf->msix_interrupts); |
409 | } |
410 | |
411 | PCI_EPF_HEADER_R(vendorid) |
412 | PCI_EPF_HEADER_W_u16(vendorid) |
413 | |
414 | PCI_EPF_HEADER_R(deviceid) |
415 | PCI_EPF_HEADER_W_u16(deviceid) |
416 | |
417 | PCI_EPF_HEADER_R(revid) |
418 | PCI_EPF_HEADER_W_u8(revid) |
419 | |
420 | PCI_EPF_HEADER_R(progif_code) |
421 | PCI_EPF_HEADER_W_u8(progif_code) |
422 | |
423 | PCI_EPF_HEADER_R(subclass_code) |
424 | PCI_EPF_HEADER_W_u8(subclass_code) |
425 | |
426 | PCI_EPF_HEADER_R(baseclass_code) |
427 | PCI_EPF_HEADER_W_u8(baseclass_code) |
428 | |
429 | PCI_EPF_HEADER_R(cache_line_size) |
430 | PCI_EPF_HEADER_W_u8(cache_line_size) |
431 | |
432 | PCI_EPF_HEADER_R(subsys_vendor_id) |
433 | PCI_EPF_HEADER_W_u16(subsys_vendor_id) |
434 | |
435 | PCI_EPF_HEADER_R(subsys_id) |
436 | PCI_EPF_HEADER_W_u16(subsys_id) |
437 | |
438 | PCI_EPF_HEADER_R(interrupt_pin) |
439 | PCI_EPF_HEADER_W_u8(interrupt_pin) |
440 | |
441 | CONFIGFS_ATTR(pci_epf_, vendorid); |
442 | CONFIGFS_ATTR(pci_epf_, deviceid); |
443 | CONFIGFS_ATTR(pci_epf_, revid); |
444 | CONFIGFS_ATTR(pci_epf_, progif_code); |
445 | CONFIGFS_ATTR(pci_epf_, subclass_code); |
446 | CONFIGFS_ATTR(pci_epf_, baseclass_code); |
447 | CONFIGFS_ATTR(pci_epf_, cache_line_size); |
448 | CONFIGFS_ATTR(pci_epf_, subsys_vendor_id); |
449 | CONFIGFS_ATTR(pci_epf_, subsys_id); |
450 | CONFIGFS_ATTR(pci_epf_, interrupt_pin); |
451 | CONFIGFS_ATTR(pci_epf_, msi_interrupts); |
452 | CONFIGFS_ATTR(pci_epf_, msix_interrupts); |
453 | |
454 | static struct configfs_attribute *pci_epf_attrs[] = { |
455 | &pci_epf_attr_vendorid, |
456 | &pci_epf_attr_deviceid, |
457 | &pci_epf_attr_revid, |
458 | &pci_epf_attr_progif_code, |
459 | &pci_epf_attr_subclass_code, |
460 | &pci_epf_attr_baseclass_code, |
461 | &pci_epf_attr_cache_line_size, |
462 | &pci_epf_attr_subsys_vendor_id, |
463 | &pci_epf_attr_subsys_id, |
464 | &pci_epf_attr_interrupt_pin, |
465 | &pci_epf_attr_msi_interrupts, |
466 | &pci_epf_attr_msix_interrupts, |
467 | NULL, |
468 | }; |
469 | |
470 | static int pci_epf_vepf_link(struct config_item *epf_pf_item, |
471 | struct config_item *epf_vf_item) |
472 | { |
473 | struct pci_epf_group *epf_vf_group = to_pci_epf_group(item: epf_vf_item); |
474 | struct pci_epf_group *epf_pf_group = to_pci_epf_group(item: epf_pf_item); |
475 | struct pci_epf *epf_pf = epf_pf_group->epf; |
476 | struct pci_epf *epf_vf = epf_vf_group->epf; |
477 | |
478 | return pci_epf_add_vepf(epf_pf, epf_vf); |
479 | } |
480 | |
481 | static void pci_epf_vepf_unlink(struct config_item *epf_pf_item, |
482 | struct config_item *epf_vf_item) |
483 | { |
484 | struct pci_epf_group *epf_vf_group = to_pci_epf_group(item: epf_vf_item); |
485 | struct pci_epf_group *epf_pf_group = to_pci_epf_group(item: epf_pf_item); |
486 | struct pci_epf *epf_pf = epf_pf_group->epf; |
487 | struct pci_epf *epf_vf = epf_vf_group->epf; |
488 | |
489 | pci_epf_remove_vepf(epf_pf, epf_vf); |
490 | } |
491 | |
492 | static void pci_epf_release(struct config_item *item) |
493 | { |
494 | struct pci_epf_group *epf_group = to_pci_epf_group(item); |
495 | |
496 | mutex_lock(&functions_mutex); |
497 | idr_remove(&functions_idr, id: epf_group->index); |
498 | mutex_unlock(lock: &functions_mutex); |
499 | pci_epf_destroy(epf: epf_group->epf); |
500 | kfree(objp: epf_group); |
501 | } |
502 | |
503 | static struct configfs_item_operations pci_epf_ops = { |
504 | .allow_link = pci_epf_vepf_link, |
505 | .drop_link = pci_epf_vepf_unlink, |
506 | .release = pci_epf_release, |
507 | }; |
508 | |
509 | static const struct config_item_type pci_epf_type = { |
510 | .ct_item_ops = &pci_epf_ops, |
511 | .ct_attrs = pci_epf_attrs, |
512 | .ct_owner = THIS_MODULE, |
513 | }; |
514 | |
515 | /** |
516 | * pci_epf_type_add_cfs() - Help function drivers to expose function specific |
517 | * attributes in configfs |
518 | * @epf: the EPF device that has to be configured using configfs |
519 | * @group: the parent configfs group (corresponding to entries in |
520 | * pci_epf_device_id) |
521 | * |
522 | * Invoke to expose function specific attributes in configfs. |
523 | * |
524 | * Return: A pointer to a config_group structure or NULL if the function driver |
525 | * does not have anything to expose (attributes configured by user) or if |
526 | * the function driver does not implement the add_cfs() method. |
527 | * |
528 | * Returns an error pointer if this function is called for an unbound EPF device |
529 | * or if the EPF driver add_cfs() method fails. |
530 | */ |
531 | static struct config_group *pci_epf_type_add_cfs(struct pci_epf *epf, |
532 | struct config_group *group) |
533 | { |
534 | struct config_group *epf_type_group; |
535 | |
536 | if (!epf->driver) { |
537 | dev_err(&epf->dev, "epf device not bound to driver\n" ); |
538 | return ERR_PTR(error: -ENODEV); |
539 | } |
540 | |
541 | if (!epf->driver->ops->add_cfs) |
542 | return NULL; |
543 | |
544 | mutex_lock(&epf->lock); |
545 | epf_type_group = epf->driver->ops->add_cfs(epf, group); |
546 | mutex_unlock(lock: &epf->lock); |
547 | |
548 | return epf_type_group; |
549 | } |
550 | |
551 | static void pci_ep_cfs_add_type_group(struct pci_epf_group *epf_group) |
552 | { |
553 | struct config_group *group; |
554 | |
555 | group = pci_epf_type_add_cfs(epf: epf_group->epf, group: &epf_group->group); |
556 | if (!group) |
557 | return; |
558 | |
559 | if (IS_ERR(ptr: group)) { |
560 | dev_err(&epf_group->epf->dev, |
561 | "failed to create epf type specific attributes\n" ); |
562 | return; |
563 | } |
564 | |
565 | configfs_register_group(parent_group: &epf_group->group, group); |
566 | } |
567 | |
568 | static void pci_epf_cfs_work(struct work_struct *work) |
569 | { |
570 | struct pci_epf_group *epf_group; |
571 | struct config_group *group; |
572 | |
573 | epf_group = container_of(work, struct pci_epf_group, cfs_work.work); |
574 | group = pci_ep_cfs_add_primary_group(epf_group); |
575 | if (IS_ERR(ptr: group)) { |
576 | pr_err("failed to create 'primary' EPC interface\n" ); |
577 | return; |
578 | } |
579 | |
580 | group = pci_ep_cfs_add_secondary_group(epf_group); |
581 | if (IS_ERR(ptr: group)) { |
582 | pr_err("failed to create 'secondary' EPC interface\n" ); |
583 | return; |
584 | } |
585 | |
586 | pci_ep_cfs_add_type_group(epf_group); |
587 | } |
588 | |
589 | static struct config_group *pci_epf_make(struct config_group *group, |
590 | const char *name) |
591 | { |
592 | struct pci_epf_group *epf_group; |
593 | struct pci_epf *epf; |
594 | char *epf_name; |
595 | int index, err; |
596 | |
597 | epf_group = kzalloc(size: sizeof(*epf_group), GFP_KERNEL); |
598 | if (!epf_group) |
599 | return ERR_PTR(error: -ENOMEM); |
600 | |
601 | mutex_lock(&functions_mutex); |
602 | index = idr_alloc(&functions_idr, ptr: epf_group, start: 0, end: 0, GFP_KERNEL); |
603 | mutex_unlock(lock: &functions_mutex); |
604 | if (index < 0) { |
605 | err = index; |
606 | goto free_group; |
607 | } |
608 | |
609 | epf_group->index = index; |
610 | |
611 | config_group_init_type_name(group: &epf_group->group, name, type: &pci_epf_type); |
612 | |
613 | epf_name = kasprintf(GFP_KERNEL, fmt: "%s.%d" , |
614 | group->cg_item.ci_name, epf_group->index); |
615 | if (!epf_name) { |
616 | err = -ENOMEM; |
617 | goto remove_idr; |
618 | } |
619 | |
620 | epf = pci_epf_create(name: epf_name); |
621 | if (IS_ERR(ptr: epf)) { |
622 | pr_err("failed to create endpoint function device\n" ); |
623 | err = -EINVAL; |
624 | goto free_name; |
625 | } |
626 | |
627 | epf->group = &epf_group->group; |
628 | epf_group->epf = epf; |
629 | |
630 | kfree(objp: epf_name); |
631 | |
632 | INIT_DELAYED_WORK(&epf_group->cfs_work, pci_epf_cfs_work); |
633 | queue_delayed_work(wq: system_wq, dwork: &epf_group->cfs_work, |
634 | delay: msecs_to_jiffies(m: 1)); |
635 | |
636 | return &epf_group->group; |
637 | |
638 | free_name: |
639 | kfree(objp: epf_name); |
640 | |
641 | remove_idr: |
642 | mutex_lock(&functions_mutex); |
643 | idr_remove(&functions_idr, id: epf_group->index); |
644 | mutex_unlock(lock: &functions_mutex); |
645 | |
646 | free_group: |
647 | kfree(objp: epf_group); |
648 | |
649 | return ERR_PTR(error: err); |
650 | } |
651 | |
652 | static void pci_epf_drop(struct config_group *group, struct config_item *item) |
653 | { |
654 | config_item_put(item); |
655 | } |
656 | |
657 | static struct configfs_group_operations pci_epf_group_ops = { |
658 | .make_group = &pci_epf_make, |
659 | .drop_item = &pci_epf_drop, |
660 | }; |
661 | |
662 | static const struct config_item_type pci_epf_group_type = { |
663 | .ct_group_ops = &pci_epf_group_ops, |
664 | .ct_owner = THIS_MODULE, |
665 | }; |
666 | |
667 | struct config_group *pci_ep_cfs_add_epf_group(const char *name) |
668 | { |
669 | struct config_group *group; |
670 | |
671 | group = configfs_register_default_group(parent_group: functions_group, name, |
672 | item_type: &pci_epf_group_type); |
673 | if (IS_ERR(ptr: group)) |
674 | pr_err("failed to register configfs group for %s function\n" , |
675 | name); |
676 | |
677 | return group; |
678 | } |
679 | EXPORT_SYMBOL(pci_ep_cfs_add_epf_group); |
680 | |
681 | void pci_ep_cfs_remove_epf_group(struct config_group *group) |
682 | { |
683 | if (IS_ERR_OR_NULL(ptr: group)) |
684 | return; |
685 | |
686 | configfs_unregister_default_group(group); |
687 | } |
688 | EXPORT_SYMBOL(pci_ep_cfs_remove_epf_group); |
689 | |
690 | static const struct config_item_type pci_functions_type = { |
691 | .ct_owner = THIS_MODULE, |
692 | }; |
693 | |
694 | static const struct config_item_type pci_controllers_type = { |
695 | .ct_owner = THIS_MODULE, |
696 | }; |
697 | |
698 | static const struct config_item_type pci_ep_type = { |
699 | .ct_owner = THIS_MODULE, |
700 | }; |
701 | |
702 | static struct configfs_subsystem pci_ep_cfs_subsys = { |
703 | .su_group = { |
704 | .cg_item = { |
705 | .ci_namebuf = "pci_ep" , |
706 | .ci_type = &pci_ep_type, |
707 | }, |
708 | }, |
709 | .su_mutex = __MUTEX_INITIALIZER(pci_ep_cfs_subsys.su_mutex), |
710 | }; |
711 | |
712 | static int __init pci_ep_cfs_init(void) |
713 | { |
714 | int ret; |
715 | struct config_group *root = &pci_ep_cfs_subsys.su_group; |
716 | |
717 | config_group_init(group: root); |
718 | |
719 | ret = configfs_register_subsystem(subsys: &pci_ep_cfs_subsys); |
720 | if (ret) { |
721 | pr_err("Error %d while registering subsystem %s\n" , |
722 | ret, root->cg_item.ci_namebuf); |
723 | goto err; |
724 | } |
725 | |
726 | functions_group = configfs_register_default_group(parent_group: root, name: "functions" , |
727 | item_type: &pci_functions_type); |
728 | if (IS_ERR(ptr: functions_group)) { |
729 | ret = PTR_ERR(ptr: functions_group); |
730 | pr_err("Error %d while registering functions group\n" , |
731 | ret); |
732 | goto err_functions_group; |
733 | } |
734 | |
735 | controllers_group = |
736 | configfs_register_default_group(parent_group: root, name: "controllers" , |
737 | item_type: &pci_controllers_type); |
738 | if (IS_ERR(ptr: controllers_group)) { |
739 | ret = PTR_ERR(ptr: controllers_group); |
740 | pr_err("Error %d while registering controllers group\n" , |
741 | ret); |
742 | goto err_controllers_group; |
743 | } |
744 | |
745 | return 0; |
746 | |
747 | err_controllers_group: |
748 | configfs_unregister_default_group(group: functions_group); |
749 | |
750 | err_functions_group: |
751 | configfs_unregister_subsystem(subsys: &pci_ep_cfs_subsys); |
752 | |
753 | err: |
754 | return ret; |
755 | } |
756 | module_init(pci_ep_cfs_init); |
757 | |
758 | static void __exit pci_ep_cfs_exit(void) |
759 | { |
760 | configfs_unregister_default_group(group: controllers_group); |
761 | configfs_unregister_default_group(group: functions_group); |
762 | configfs_unregister_subsystem(subsys: &pci_ep_cfs_subsys); |
763 | } |
764 | module_exit(pci_ep_cfs_exit); |
765 | |
766 | MODULE_DESCRIPTION("PCI EP CONFIGFS" ); |
767 | MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>" ); |
768 | |