1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * fsl-mc object allocator driver |
4 | * |
5 | * Copyright (C) 2013-2016 Freescale Semiconductor, Inc. |
6 | * |
7 | */ |
8 | |
9 | #include <linux/module.h> |
10 | #include <linux/msi.h> |
11 | #include <linux/fsl/mc.h> |
12 | |
13 | #include "fsl-mc-private.h" |
14 | |
15 | static bool __must_check fsl_mc_is_allocatable(struct fsl_mc_device *mc_dev) |
16 | { |
17 | return is_fsl_mc_bus_dpbp(mc_dev) || |
18 | is_fsl_mc_bus_dpmcp(mc_dev) || |
19 | is_fsl_mc_bus_dpcon(mc_dev); |
20 | } |
21 | |
22 | /** |
23 | * fsl_mc_resource_pool_add_device - add allocatable object to a resource |
24 | * pool of a given fsl-mc bus |
25 | * |
26 | * @mc_bus: pointer to the fsl-mc bus |
27 | * @pool_type: pool type |
28 | * @mc_dev: pointer to allocatable fsl-mc device |
29 | */ |
30 | static int __must_check fsl_mc_resource_pool_add_device(struct fsl_mc_bus |
31 | *mc_bus, |
32 | enum fsl_mc_pool_type |
33 | pool_type, |
34 | struct fsl_mc_device |
35 | *mc_dev) |
36 | { |
37 | struct fsl_mc_resource_pool *res_pool; |
38 | struct fsl_mc_resource *resource; |
39 | struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; |
40 | int error = -EINVAL; |
41 | |
42 | if (pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES) |
43 | goto out; |
44 | if (!fsl_mc_is_allocatable(mc_dev)) |
45 | goto out; |
46 | if (mc_dev->resource) |
47 | goto out; |
48 | |
49 | res_pool = &mc_bus->resource_pools[pool_type]; |
50 | if (res_pool->type != pool_type) |
51 | goto out; |
52 | if (res_pool->mc_bus != mc_bus) |
53 | goto out; |
54 | |
55 | mutex_lock(&res_pool->mutex); |
56 | |
57 | if (res_pool->max_count < 0) |
58 | goto out_unlock; |
59 | if (res_pool->free_count < 0 || |
60 | res_pool->free_count > res_pool->max_count) |
61 | goto out_unlock; |
62 | |
63 | resource = devm_kzalloc(dev: &mc_bus_dev->dev, size: sizeof(*resource), |
64 | GFP_KERNEL); |
65 | if (!resource) { |
66 | error = -ENOMEM; |
67 | dev_err(&mc_bus_dev->dev, |
68 | "Failed to allocate memory for fsl_mc_resource\n" ); |
69 | goto out_unlock; |
70 | } |
71 | |
72 | resource->type = pool_type; |
73 | resource->id = mc_dev->obj_desc.id; |
74 | resource->data = mc_dev; |
75 | resource->parent_pool = res_pool; |
76 | INIT_LIST_HEAD(list: &resource->node); |
77 | list_add_tail(new: &resource->node, head: &res_pool->free_list); |
78 | mc_dev->resource = resource; |
79 | res_pool->free_count++; |
80 | res_pool->max_count++; |
81 | error = 0; |
82 | out_unlock: |
83 | mutex_unlock(lock: &res_pool->mutex); |
84 | out: |
85 | return error; |
86 | } |
87 | |
88 | /** |
89 | * fsl_mc_resource_pool_remove_device - remove an allocatable device from a |
90 | * resource pool |
91 | * |
92 | * @mc_dev: pointer to allocatable fsl-mc device |
93 | * |
94 | * It permanently removes an allocatable fsl-mc device from the resource |
95 | * pool. It's an error if the device is in use. |
96 | */ |
97 | static int __must_check fsl_mc_resource_pool_remove_device(struct fsl_mc_device |
98 | *mc_dev) |
99 | { |
100 | struct fsl_mc_device *mc_bus_dev; |
101 | struct fsl_mc_bus *mc_bus; |
102 | struct fsl_mc_resource_pool *res_pool; |
103 | struct fsl_mc_resource *resource; |
104 | int error = -EINVAL; |
105 | |
106 | mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); |
107 | mc_bus = to_fsl_mc_bus(mc_bus_dev); |
108 | |
109 | resource = mc_dev->resource; |
110 | if (!resource || resource->data != mc_dev) { |
111 | dev_err(&mc_bus_dev->dev, "resource mismatch\n" ); |
112 | goto out; |
113 | } |
114 | |
115 | res_pool = resource->parent_pool; |
116 | if (res_pool != &mc_bus->resource_pools[resource->type]) { |
117 | dev_err(&mc_bus_dev->dev, "pool mismatch\n" ); |
118 | goto out; |
119 | } |
120 | |
121 | mutex_lock(&res_pool->mutex); |
122 | |
123 | if (res_pool->max_count <= 0) { |
124 | dev_err(&mc_bus_dev->dev, "max_count underflow\n" ); |
125 | goto out_unlock; |
126 | } |
127 | if (res_pool->free_count <= 0 || |
128 | res_pool->free_count > res_pool->max_count) { |
129 | dev_err(&mc_bus_dev->dev, "free_count mismatch\n" ); |
130 | goto out_unlock; |
131 | } |
132 | |
133 | /* |
134 | * If the device is currently allocated, its resource is not |
135 | * in the free list and thus, the device cannot be removed. |
136 | */ |
137 | if (list_empty(head: &resource->node)) { |
138 | error = -EBUSY; |
139 | dev_err(&mc_bus_dev->dev, |
140 | "Device %s cannot be removed from resource pool\n" , |
141 | dev_name(&mc_dev->dev)); |
142 | goto out_unlock; |
143 | } |
144 | |
145 | list_del_init(entry: &resource->node); |
146 | res_pool->free_count--; |
147 | res_pool->max_count--; |
148 | |
149 | devm_kfree(dev: &mc_bus_dev->dev, p: resource); |
150 | mc_dev->resource = NULL; |
151 | error = 0; |
152 | out_unlock: |
153 | mutex_unlock(lock: &res_pool->mutex); |
154 | out: |
155 | return error; |
156 | } |
157 | |
158 | static const char *const fsl_mc_pool_type_strings[] = { |
159 | [FSL_MC_POOL_DPMCP] = "dpmcp" , |
160 | [FSL_MC_POOL_DPBP] = "dpbp" , |
161 | [FSL_MC_POOL_DPCON] = "dpcon" , |
162 | [FSL_MC_POOL_IRQ] = "irq" , |
163 | }; |
164 | |
165 | static int __must_check object_type_to_pool_type(const char *object_type, |
166 | enum fsl_mc_pool_type |
167 | *pool_type) |
168 | { |
169 | unsigned int i; |
170 | |
171 | for (i = 0; i < ARRAY_SIZE(fsl_mc_pool_type_strings); i++) { |
172 | if (strcmp(object_type, fsl_mc_pool_type_strings[i]) == 0) { |
173 | *pool_type = i; |
174 | return 0; |
175 | } |
176 | } |
177 | |
178 | return -EINVAL; |
179 | } |
180 | |
181 | int __must_check fsl_mc_resource_allocate(struct fsl_mc_bus *mc_bus, |
182 | enum fsl_mc_pool_type pool_type, |
183 | struct fsl_mc_resource **new_resource) |
184 | { |
185 | struct fsl_mc_resource_pool *res_pool; |
186 | struct fsl_mc_resource *resource; |
187 | struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; |
188 | int error = -EINVAL; |
189 | |
190 | BUILD_BUG_ON(ARRAY_SIZE(fsl_mc_pool_type_strings) != |
191 | FSL_MC_NUM_POOL_TYPES); |
192 | |
193 | *new_resource = NULL; |
194 | if (pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES) |
195 | goto out; |
196 | |
197 | res_pool = &mc_bus->resource_pools[pool_type]; |
198 | if (res_pool->mc_bus != mc_bus) |
199 | goto out; |
200 | |
201 | mutex_lock(&res_pool->mutex); |
202 | resource = list_first_entry_or_null(&res_pool->free_list, |
203 | struct fsl_mc_resource, node); |
204 | |
205 | if (!resource) { |
206 | error = -ENXIO; |
207 | dev_err(&mc_bus_dev->dev, |
208 | "No more resources of type %s left\n" , |
209 | fsl_mc_pool_type_strings[pool_type]); |
210 | goto out_unlock; |
211 | } |
212 | |
213 | if (resource->type != pool_type) |
214 | goto out_unlock; |
215 | if (resource->parent_pool != res_pool) |
216 | goto out_unlock; |
217 | if (res_pool->free_count <= 0 || |
218 | res_pool->free_count > res_pool->max_count) |
219 | goto out_unlock; |
220 | |
221 | list_del_init(entry: &resource->node); |
222 | |
223 | res_pool->free_count--; |
224 | error = 0; |
225 | out_unlock: |
226 | mutex_unlock(lock: &res_pool->mutex); |
227 | *new_resource = resource; |
228 | out: |
229 | return error; |
230 | } |
231 | EXPORT_SYMBOL_GPL(fsl_mc_resource_allocate); |
232 | |
233 | void fsl_mc_resource_free(struct fsl_mc_resource *resource) |
234 | { |
235 | struct fsl_mc_resource_pool *res_pool; |
236 | |
237 | res_pool = resource->parent_pool; |
238 | if (resource->type != res_pool->type) |
239 | return; |
240 | |
241 | mutex_lock(&res_pool->mutex); |
242 | if (res_pool->free_count < 0 || |
243 | res_pool->free_count >= res_pool->max_count) |
244 | goto out_unlock; |
245 | |
246 | if (!list_empty(head: &resource->node)) |
247 | goto out_unlock; |
248 | |
249 | list_add_tail(new: &resource->node, head: &res_pool->free_list); |
250 | res_pool->free_count++; |
251 | out_unlock: |
252 | mutex_unlock(lock: &res_pool->mutex); |
253 | } |
254 | EXPORT_SYMBOL_GPL(fsl_mc_resource_free); |
255 | |
256 | /** |
257 | * fsl_mc_object_allocate - Allocates an fsl-mc object of the given |
258 | * pool type from a given fsl-mc bus instance |
259 | * |
260 | * @mc_dev: fsl-mc device which is used in conjunction with the |
261 | * allocated object |
262 | * @pool_type: pool type |
263 | * @new_mc_adev: pointer to area where the pointer to the allocated device |
264 | * is to be returned |
265 | * |
266 | * Allocatable objects are always used in conjunction with some functional |
267 | * device. This function allocates an object of the specified type from |
268 | * the DPRC containing the functional device. |
269 | * |
270 | * NOTE: pool_type must be different from FSL_MC_POOL_MCP, since MC |
271 | * portals are allocated using fsl_mc_portal_allocate(), instead of |
272 | * this function. |
273 | */ |
274 | int __must_check fsl_mc_object_allocate(struct fsl_mc_device *mc_dev, |
275 | enum fsl_mc_pool_type pool_type, |
276 | struct fsl_mc_device **new_mc_adev) |
277 | { |
278 | struct fsl_mc_device *mc_bus_dev; |
279 | struct fsl_mc_bus *mc_bus; |
280 | struct fsl_mc_device *mc_adev; |
281 | int error = -EINVAL; |
282 | struct fsl_mc_resource *resource = NULL; |
283 | |
284 | *new_mc_adev = NULL; |
285 | if (mc_dev->flags & FSL_MC_IS_DPRC) |
286 | goto error; |
287 | |
288 | if (!dev_is_fsl_mc(mc_dev->dev.parent)) |
289 | goto error; |
290 | |
291 | if (pool_type == FSL_MC_POOL_DPMCP) |
292 | goto error; |
293 | |
294 | mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); |
295 | mc_bus = to_fsl_mc_bus(mc_bus_dev); |
296 | error = fsl_mc_resource_allocate(mc_bus, pool_type, &resource); |
297 | if (error < 0) |
298 | goto error; |
299 | |
300 | mc_adev = resource->data; |
301 | if (!mc_adev) { |
302 | error = -EINVAL; |
303 | goto error; |
304 | } |
305 | |
306 | mc_adev->consumer_link = device_link_add(consumer: &mc_dev->dev, |
307 | supplier: &mc_adev->dev, |
308 | DL_FLAG_AUTOREMOVE_CONSUMER); |
309 | if (!mc_adev->consumer_link) { |
310 | error = -EINVAL; |
311 | goto error; |
312 | } |
313 | |
314 | *new_mc_adev = mc_adev; |
315 | return 0; |
316 | error: |
317 | if (resource) |
318 | fsl_mc_resource_free(resource); |
319 | |
320 | return error; |
321 | } |
322 | EXPORT_SYMBOL_GPL(fsl_mc_object_allocate); |
323 | |
324 | /** |
325 | * fsl_mc_object_free - Returns an fsl-mc object to the resource |
326 | * pool where it came from. |
327 | * @mc_adev: Pointer to the fsl-mc device |
328 | */ |
329 | void fsl_mc_object_free(struct fsl_mc_device *mc_adev) |
330 | { |
331 | struct fsl_mc_resource *resource; |
332 | |
333 | resource = mc_adev->resource; |
334 | if (resource->type == FSL_MC_POOL_DPMCP) |
335 | return; |
336 | if (resource->data != mc_adev) |
337 | return; |
338 | |
339 | fsl_mc_resource_free(resource); |
340 | |
341 | mc_adev->consumer_link = NULL; |
342 | } |
343 | EXPORT_SYMBOL_GPL(fsl_mc_object_free); |
344 | |
345 | /* |
346 | * A DPRC and the devices in the DPRC all share the same GIC-ITS device |
347 | * ID. A block of IRQs is pre-allocated and maintained in a pool |
348 | * from which devices can allocate them when needed. |
349 | */ |
350 | |
351 | /* |
352 | * Initialize the interrupt pool associated with an fsl-mc bus. |
353 | * It allocates a block of IRQs from the GIC-ITS. |
354 | */ |
355 | int fsl_mc_populate_irq_pool(struct fsl_mc_device *mc_bus_dev, |
356 | unsigned int irq_count) |
357 | { |
358 | unsigned int i; |
359 | struct fsl_mc_device_irq *irq_resources; |
360 | struct fsl_mc_device_irq *mc_dev_irq; |
361 | int error; |
362 | struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); |
363 | struct fsl_mc_resource_pool *res_pool = |
364 | &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; |
365 | |
366 | /* do nothing if the IRQ pool is already populated */ |
367 | if (mc_bus->irq_resources) |
368 | return 0; |
369 | |
370 | if (irq_count == 0 || |
371 | irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) |
372 | return -EINVAL; |
373 | |
374 | error = fsl_mc_msi_domain_alloc_irqs(dev: &mc_bus_dev->dev, irq_count); |
375 | if (error < 0) |
376 | return error; |
377 | |
378 | irq_resources = devm_kcalloc(dev: &mc_bus_dev->dev, |
379 | n: irq_count, size: sizeof(*irq_resources), |
380 | GFP_KERNEL); |
381 | if (!irq_resources) { |
382 | error = -ENOMEM; |
383 | goto cleanup_msi_irqs; |
384 | } |
385 | |
386 | for (i = 0; i < irq_count; i++) { |
387 | mc_dev_irq = &irq_resources[i]; |
388 | |
389 | /* |
390 | * NOTE: This mc_dev_irq's MSI addr/value pair will be set |
391 | * by the fsl_mc_msi_write_msg() callback |
392 | */ |
393 | mc_dev_irq->resource.type = res_pool->type; |
394 | mc_dev_irq->resource.data = mc_dev_irq; |
395 | mc_dev_irq->resource.parent_pool = res_pool; |
396 | mc_dev_irq->virq = msi_get_virq(dev: &mc_bus_dev->dev, index: i); |
397 | mc_dev_irq->resource.id = mc_dev_irq->virq; |
398 | INIT_LIST_HEAD(list: &mc_dev_irq->resource.node); |
399 | list_add_tail(new: &mc_dev_irq->resource.node, head: &res_pool->free_list); |
400 | } |
401 | |
402 | res_pool->max_count = irq_count; |
403 | res_pool->free_count = irq_count; |
404 | mc_bus->irq_resources = irq_resources; |
405 | return 0; |
406 | |
407 | cleanup_msi_irqs: |
408 | fsl_mc_msi_domain_free_irqs(dev: &mc_bus_dev->dev); |
409 | return error; |
410 | } |
411 | EXPORT_SYMBOL_GPL(fsl_mc_populate_irq_pool); |
412 | |
413 | /* |
414 | * Teardown the interrupt pool associated with an fsl-mc bus. |
415 | * It frees the IRQs that were allocated to the pool, back to the GIC-ITS. |
416 | */ |
417 | void fsl_mc_cleanup_irq_pool(struct fsl_mc_device *mc_bus_dev) |
418 | { |
419 | struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); |
420 | struct fsl_mc_resource_pool *res_pool = |
421 | &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; |
422 | |
423 | if (!mc_bus->irq_resources) |
424 | return; |
425 | |
426 | if (res_pool->max_count == 0) |
427 | return; |
428 | |
429 | if (res_pool->free_count != res_pool->max_count) |
430 | return; |
431 | |
432 | INIT_LIST_HEAD(list: &res_pool->free_list); |
433 | res_pool->max_count = 0; |
434 | res_pool->free_count = 0; |
435 | mc_bus->irq_resources = NULL; |
436 | fsl_mc_msi_domain_free_irqs(dev: &mc_bus_dev->dev); |
437 | } |
438 | EXPORT_SYMBOL_GPL(fsl_mc_cleanup_irq_pool); |
439 | |
440 | /* |
441 | * Allocate the IRQs required by a given fsl-mc device. |
442 | */ |
443 | int __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev) |
444 | { |
445 | int i; |
446 | int irq_count; |
447 | int res_allocated_count = 0; |
448 | int error = -EINVAL; |
449 | struct fsl_mc_device_irq **irqs = NULL; |
450 | struct fsl_mc_bus *mc_bus; |
451 | struct fsl_mc_resource_pool *res_pool; |
452 | |
453 | if (mc_dev->irqs) |
454 | return -EINVAL; |
455 | |
456 | irq_count = mc_dev->obj_desc.irq_count; |
457 | if (irq_count == 0) |
458 | return -EINVAL; |
459 | |
460 | if (is_fsl_mc_bus_dprc(mc_dev)) |
461 | mc_bus = to_fsl_mc_bus(mc_dev); |
462 | else |
463 | mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent)); |
464 | |
465 | if (!mc_bus->irq_resources) |
466 | return -EINVAL; |
467 | |
468 | res_pool = &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; |
469 | if (res_pool->free_count < irq_count) { |
470 | dev_err(&mc_dev->dev, |
471 | "Not able to allocate %u irqs for device\n" , irq_count); |
472 | return -ENOSPC; |
473 | } |
474 | |
475 | irqs = devm_kcalloc(dev: &mc_dev->dev, n: irq_count, size: sizeof(irqs[0]), |
476 | GFP_KERNEL); |
477 | if (!irqs) |
478 | return -ENOMEM; |
479 | |
480 | for (i = 0; i < irq_count; i++) { |
481 | struct fsl_mc_resource *resource; |
482 | |
483 | error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_IRQ, |
484 | &resource); |
485 | if (error < 0) |
486 | goto error_resource_alloc; |
487 | |
488 | irqs[i] = to_fsl_mc_irq(resource); |
489 | res_allocated_count++; |
490 | |
491 | irqs[i]->mc_dev = mc_dev; |
492 | irqs[i]->dev_irq_index = i; |
493 | } |
494 | |
495 | mc_dev->irqs = irqs; |
496 | return 0; |
497 | |
498 | error_resource_alloc: |
499 | for (i = 0; i < res_allocated_count; i++) { |
500 | irqs[i]->mc_dev = NULL; |
501 | fsl_mc_resource_free(&irqs[i]->resource); |
502 | } |
503 | |
504 | return error; |
505 | } |
506 | EXPORT_SYMBOL_GPL(fsl_mc_allocate_irqs); |
507 | |
508 | /* |
509 | * Frees the IRQs that were allocated for an fsl-mc device. |
510 | */ |
511 | void fsl_mc_free_irqs(struct fsl_mc_device *mc_dev) |
512 | { |
513 | int i; |
514 | int irq_count; |
515 | struct fsl_mc_bus *mc_bus; |
516 | struct fsl_mc_device_irq **irqs = mc_dev->irqs; |
517 | |
518 | if (!irqs) |
519 | return; |
520 | |
521 | irq_count = mc_dev->obj_desc.irq_count; |
522 | |
523 | if (is_fsl_mc_bus_dprc(mc_dev)) |
524 | mc_bus = to_fsl_mc_bus(mc_dev); |
525 | else |
526 | mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent)); |
527 | |
528 | if (!mc_bus->irq_resources) |
529 | return; |
530 | |
531 | for (i = 0; i < irq_count; i++) { |
532 | irqs[i]->mc_dev = NULL; |
533 | fsl_mc_resource_free(&irqs[i]->resource); |
534 | } |
535 | |
536 | mc_dev->irqs = NULL; |
537 | } |
538 | EXPORT_SYMBOL_GPL(fsl_mc_free_irqs); |
539 | |
540 | void fsl_mc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev) |
541 | { |
542 | int pool_type; |
543 | struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); |
544 | |
545 | for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++) { |
546 | struct fsl_mc_resource_pool *res_pool = |
547 | &mc_bus->resource_pools[pool_type]; |
548 | |
549 | res_pool->type = pool_type; |
550 | res_pool->max_count = 0; |
551 | res_pool->free_count = 0; |
552 | res_pool->mc_bus = mc_bus; |
553 | INIT_LIST_HEAD(list: &res_pool->free_list); |
554 | mutex_init(&res_pool->mutex); |
555 | } |
556 | } |
557 | |
558 | static void fsl_mc_cleanup_resource_pool(struct fsl_mc_device *mc_bus_dev, |
559 | enum fsl_mc_pool_type pool_type) |
560 | { |
561 | struct fsl_mc_resource *resource; |
562 | struct fsl_mc_resource *next; |
563 | struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); |
564 | struct fsl_mc_resource_pool *res_pool = |
565 | &mc_bus->resource_pools[pool_type]; |
566 | |
567 | list_for_each_entry_safe(resource, next, &res_pool->free_list, node) |
568 | devm_kfree(dev: &mc_bus_dev->dev, p: resource); |
569 | } |
570 | |
571 | void fsl_mc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev) |
572 | { |
573 | int pool_type; |
574 | |
575 | for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++) |
576 | fsl_mc_cleanup_resource_pool(mc_bus_dev, pool_type); |
577 | } |
578 | |
579 | /* |
580 | * fsl_mc_allocator_probe - callback invoked when an allocatable device is |
581 | * being added to the system |
582 | */ |
583 | static int fsl_mc_allocator_probe(struct fsl_mc_device *mc_dev) |
584 | { |
585 | enum fsl_mc_pool_type pool_type; |
586 | struct fsl_mc_device *mc_bus_dev; |
587 | struct fsl_mc_bus *mc_bus; |
588 | int error; |
589 | |
590 | if (!fsl_mc_is_allocatable(mc_dev)) |
591 | return -EINVAL; |
592 | |
593 | mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); |
594 | if (!dev_is_fsl_mc(&mc_bus_dev->dev)) |
595 | return -EINVAL; |
596 | |
597 | mc_bus = to_fsl_mc_bus(mc_bus_dev); |
598 | error = object_type_to_pool_type(object_type: mc_dev->obj_desc.type, pool_type: &pool_type); |
599 | if (error < 0) |
600 | return error; |
601 | |
602 | error = fsl_mc_resource_pool_add_device(mc_bus, pool_type, mc_dev); |
603 | if (error < 0) |
604 | return error; |
605 | |
606 | dev_dbg(&mc_dev->dev, |
607 | "Allocatable fsl-mc device bound to fsl_mc_allocator driver" ); |
608 | return 0; |
609 | } |
610 | |
611 | /* |
612 | * fsl_mc_allocator_remove - callback invoked when an allocatable device is |
613 | * being removed from the system |
614 | */ |
615 | static void fsl_mc_allocator_remove(struct fsl_mc_device *mc_dev) |
616 | { |
617 | int error; |
618 | |
619 | if (mc_dev->resource) { |
620 | error = fsl_mc_resource_pool_remove_device(mc_dev); |
621 | if (error < 0) |
622 | return; |
623 | } |
624 | |
625 | dev_dbg(&mc_dev->dev, |
626 | "Allocatable fsl-mc device unbound from fsl_mc_allocator driver" ); |
627 | } |
628 | |
629 | static const struct fsl_mc_device_id match_id_table[] = { |
630 | { |
631 | .vendor = FSL_MC_VENDOR_FREESCALE, |
632 | .obj_type = "dpbp" , |
633 | }, |
634 | { |
635 | .vendor = FSL_MC_VENDOR_FREESCALE, |
636 | .obj_type = "dpmcp" , |
637 | }, |
638 | { |
639 | .vendor = FSL_MC_VENDOR_FREESCALE, |
640 | .obj_type = "dpcon" , |
641 | }, |
642 | {.vendor = 0x0}, |
643 | }; |
644 | |
645 | static struct fsl_mc_driver fsl_mc_allocator_driver = { |
646 | .driver = { |
647 | .name = "fsl_mc_allocator" , |
648 | .pm = NULL, |
649 | }, |
650 | .match_id_table = match_id_table, |
651 | .probe = fsl_mc_allocator_probe, |
652 | .remove = fsl_mc_allocator_remove, |
653 | }; |
654 | |
655 | int __init fsl_mc_allocator_driver_init(void) |
656 | { |
657 | return fsl_mc_driver_register(&fsl_mc_allocator_driver); |
658 | } |
659 | |
660 | void fsl_mc_allocator_driver_exit(void) |
661 | { |
662 | fsl_mc_driver_unregister(driver: &fsl_mc_allocator_driver); |
663 | } |
664 | |