991273f956ce717840bf7f4331899586476e0d7e
[platform/kernel/linux-starfive.git] / drivers / bus / fsl-mc / fsl-mc-allocator.c
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(&mc_bus_dev->dev, 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(&resource->node);
77         list_add_tail(&resource->node, &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(&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(&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(&resource->node);
146         res_pool->free_count--;
147         res_pool->max_count--;
148
149         devm_kfree(&mc_bus_dev->dev, resource);
150         mc_dev->resource = NULL;
151         error = 0;
152 out_unlock:
153         mutex_unlock(&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(&resource->node);
222
223         res_pool->free_count--;
224         error = 0;
225 out_unlock:
226         mutex_unlock(&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(&resource->node))
247                 goto out_unlock;
248
249         list_add_tail(&resource->node, &res_pool->free_list);
250         res_pool->free_count++;
251 out_unlock:
252         mutex_unlock(&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(&mc_dev->dev,
307                                                  &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(&mc_bus_dev->dev, irq_count);
375         if (error < 0)
376                 return error;
377
378         irq_resources = devm_kcalloc(&mc_bus_dev->dev,
379                                      irq_count, 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(&mc_bus_dev->dev, i);
397                 mc_dev_irq->resource.id = mc_dev_irq->virq;
398                 INIT_LIST_HEAD(&mc_dev_irq->resource.node);
399                 list_add_tail(&mc_dev_irq->resource.node, &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(&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(&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(&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(&mc_dev->dev, irq_count, 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(&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         int free_count = 0;
567
568         list_for_each_entry_safe(resource, next, &res_pool->free_list, node) {
569                 free_count++;
570                 devm_kfree(&mc_bus_dev->dev, resource);
571         }
572 }
573
574 void fsl_mc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev)
575 {
576         int pool_type;
577
578         for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++)
579                 fsl_mc_cleanup_resource_pool(mc_bus_dev, pool_type);
580 }
581
582 /*
583  * fsl_mc_allocator_probe - callback invoked when an allocatable device is
584  * being added to the system
585  */
586 static int fsl_mc_allocator_probe(struct fsl_mc_device *mc_dev)
587 {
588         enum fsl_mc_pool_type pool_type;
589         struct fsl_mc_device *mc_bus_dev;
590         struct fsl_mc_bus *mc_bus;
591         int error;
592
593         if (!fsl_mc_is_allocatable(mc_dev))
594                 return -EINVAL;
595
596         mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
597         if (!dev_is_fsl_mc(&mc_bus_dev->dev))
598                 return -EINVAL;
599
600         mc_bus = to_fsl_mc_bus(mc_bus_dev);
601         error = object_type_to_pool_type(mc_dev->obj_desc.type, &pool_type);
602         if (error < 0)
603                 return error;
604
605         error = fsl_mc_resource_pool_add_device(mc_bus, pool_type, mc_dev);
606         if (error < 0)
607                 return error;
608
609         dev_dbg(&mc_dev->dev,
610                 "Allocatable fsl-mc device bound to fsl_mc_allocator driver");
611         return 0;
612 }
613
614 /*
615  * fsl_mc_allocator_remove - callback invoked when an allocatable device is
616  * being removed from the system
617  */
618 static void fsl_mc_allocator_remove(struct fsl_mc_device *mc_dev)
619 {
620         int error;
621
622         if (mc_dev->resource) {
623                 error = fsl_mc_resource_pool_remove_device(mc_dev);
624                 if (error < 0)
625                         return;
626         }
627
628         dev_dbg(&mc_dev->dev,
629                 "Allocatable fsl-mc device unbound from fsl_mc_allocator driver");
630 }
631
632 static const struct fsl_mc_device_id match_id_table[] = {
633         {
634          .vendor = FSL_MC_VENDOR_FREESCALE,
635          .obj_type = "dpbp",
636         },
637         {
638          .vendor = FSL_MC_VENDOR_FREESCALE,
639          .obj_type = "dpmcp",
640         },
641         {
642          .vendor = FSL_MC_VENDOR_FREESCALE,
643          .obj_type = "dpcon",
644         },
645         {.vendor = 0x0},
646 };
647
648 static struct fsl_mc_driver fsl_mc_allocator_driver = {
649         .driver = {
650                    .name = "fsl_mc_allocator",
651                    .pm = NULL,
652                    },
653         .match_id_table = match_id_table,
654         .probe = fsl_mc_allocator_probe,
655         .remove = fsl_mc_allocator_remove,
656 };
657
658 int __init fsl_mc_allocator_driver_init(void)
659 {
660         return fsl_mc_driver_register(&fsl_mc_allocator_driver);
661 }
662
663 void fsl_mc_allocator_driver_exit(void)
664 {
665         fsl_mc_driver_unregister(&fsl_mc_allocator_driver);
666 }