Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / staging / fsl-mc / bus / mc-allocator.c
1 /*
2  * Freescale MC object device allocator driver
3  *
4  * Copyright (C) 2013 Freescale Semiconductor, Inc.
5  *
6  * This file is licensed under the terms of the GNU General Public
7  * License version 2. This program is licensed "as is" without any
8  * warranty of any kind, whether express or implied.
9  */
10
11 #include "../include/mc-private.h"
12 #include "../include/mc-sys.h"
13 #include <linux/module.h>
14 #include "../include/dpbp-cmd.h"
15 #include "../include/dpcon-cmd.h"
16 #include "dpmcp-cmd.h"
17 #include "dpmcp.h"
18
19 /**
20  * fsl_mc_resource_pool_add_device - add allocatable device to a resource
21  * pool of a given MC bus
22  *
23  * @mc_bus: pointer to the MC bus
24  * @pool_type: MC bus pool type
25  * @mc_dev: Pointer to allocatable MC object device
26  *
27  * It adds an allocatable MC object device to a container's resource pool of
28  * the given resource type
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         bool mutex_locked = false;
42
43         if (WARN_ON(pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES))
44                 goto out;
45         if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
46                 goto out;
47         if (WARN_ON(mc_dev->resource))
48                 goto out;
49
50         res_pool = &mc_bus->resource_pools[pool_type];
51         if (WARN_ON(res_pool->type != pool_type))
52                 goto out;
53         if (WARN_ON(res_pool->mc_bus != mc_bus))
54                 goto out;
55
56         mutex_lock(&res_pool->mutex);
57         mutex_locked = true;
58
59         if (WARN_ON(res_pool->max_count < 0))
60                 goto out;
61         if (WARN_ON(res_pool->free_count < 0 ||
62                     res_pool->free_count > res_pool->max_count))
63                 goto out;
64
65         resource = devm_kzalloc(&mc_bus_dev->dev, sizeof(*resource),
66                                 GFP_KERNEL);
67         if (!resource) {
68                 error = -ENOMEM;
69                 dev_err(&mc_bus_dev->dev,
70                         "Failed to allocate memory for fsl_mc_resource\n");
71                 goto out;
72         }
73
74         resource->type = pool_type;
75         resource->id = mc_dev->obj_desc.id;
76         resource->data = mc_dev;
77         resource->parent_pool = res_pool;
78         INIT_LIST_HEAD(&resource->node);
79         list_add_tail(&resource->node, &res_pool->free_list);
80         mc_dev->resource = resource;
81         res_pool->free_count++;
82         res_pool->max_count++;
83         error = 0;
84 out:
85         if (mutex_locked)
86                 mutex_unlock(&res_pool->mutex);
87
88         return error;
89 }
90
91 /**
92  * fsl_mc_resource_pool_remove_device - remove an allocatable device from a
93  * resource pool
94  *
95  * @mc_dev: Pointer to allocatable MC object device
96  *
97  * It permanently removes an allocatable MC object device from the resource
98  * pool, the device is currently in, as long as it is in the pool's free list.
99  */
100 static int __must_check fsl_mc_resource_pool_remove_device(struct fsl_mc_device
101                                                                    *mc_dev)
102 {
103         struct fsl_mc_device *mc_bus_dev;
104         struct fsl_mc_bus *mc_bus;
105         struct fsl_mc_resource_pool *res_pool;
106         struct fsl_mc_resource *resource;
107         int error = -EINVAL;
108         bool mutex_locked = false;
109
110         if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
111                 goto out;
112
113         resource = mc_dev->resource;
114         if (WARN_ON(resource->data != mc_dev))
115                 goto out;
116
117         mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
118         mc_bus = to_fsl_mc_bus(mc_bus_dev);
119         res_pool = resource->parent_pool;
120         if (WARN_ON(res_pool != &mc_bus->resource_pools[resource->type]))
121                 goto out;
122
123         mutex_lock(&res_pool->mutex);
124         mutex_locked = true;
125
126         if (WARN_ON(res_pool->max_count <= 0))
127                 goto out;
128         if (WARN_ON(res_pool->free_count <= 0 ||
129                     res_pool->free_count > res_pool->max_count))
130                 goto out;
131
132         /*
133          * If the device is currently allocated, its resource is not
134          * in the free list and thus, the device cannot be removed.
135          */
136         if (list_empty(&resource->node)) {
137                 error = -EBUSY;
138                 dev_err(&mc_bus_dev->dev,
139                         "Device %s cannot be removed from resource pool\n",
140                         dev_name(&mc_dev->dev));
141                 goto out;
142         }
143
144         list_del(&resource->node);
145         INIT_LIST_HEAD(&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:
153         if (mutex_locked)
154                 mutex_unlock(&res_pool->mutex);
155
156         return error;
157 }
158
159 static const char *const fsl_mc_pool_type_strings[] = {
160         [FSL_MC_POOL_DPMCP] = "dpmcp",
161         [FSL_MC_POOL_DPBP] = "dpbp",
162         [FSL_MC_POOL_DPCON] = "dpcon",
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         bool mutex_locked = false;
190
191         BUILD_BUG_ON(ARRAY_SIZE(fsl_mc_pool_type_strings) !=
192                      FSL_MC_NUM_POOL_TYPES);
193
194         *new_resource = NULL;
195         if (WARN_ON(pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES))
196                 goto error;
197
198         res_pool = &mc_bus->resource_pools[pool_type];
199         if (WARN_ON(res_pool->mc_bus != mc_bus))
200                 goto error;
201
202         mutex_lock(&res_pool->mutex);
203         mutex_locked = true;
204         resource = list_first_entry_or_null(&res_pool->free_list,
205                                             struct fsl_mc_resource, node);
206
207         if (!resource) {
208                 WARN_ON(res_pool->free_count != 0);
209                 error = -ENXIO;
210                 dev_err(&mc_bus_dev->dev,
211                         "No more resources of type %s left\n",
212                         fsl_mc_pool_type_strings[pool_type]);
213                 goto error;
214         }
215
216         if (WARN_ON(resource->type != pool_type))
217                 goto error;
218         if (WARN_ON(resource->parent_pool != res_pool))
219                 goto error;
220         if (WARN_ON(res_pool->free_count <= 0 ||
221                     res_pool->free_count > res_pool->max_count))
222                 goto error;
223
224         list_del(&resource->node);
225         INIT_LIST_HEAD(&resource->node);
226
227         res_pool->free_count--;
228         mutex_unlock(&res_pool->mutex);
229         *new_resource = resource;
230         return 0;
231 error:
232         if (mutex_locked)
233                 mutex_unlock(&res_pool->mutex);
234
235         return error;
236 }
237 EXPORT_SYMBOL_GPL(fsl_mc_resource_allocate);
238
239 void fsl_mc_resource_free(struct fsl_mc_resource *resource)
240 {
241         struct fsl_mc_resource_pool *res_pool;
242         bool mutex_locked = false;
243
244         res_pool = resource->parent_pool;
245         if (WARN_ON(resource->type != res_pool->type))
246                 goto out;
247
248         mutex_lock(&res_pool->mutex);
249         mutex_locked = true;
250         if (WARN_ON(res_pool->free_count < 0 ||
251                     res_pool->free_count >= res_pool->max_count))
252                 goto out;
253
254         if (WARN_ON(!list_empty(&resource->node)))
255                 goto out;
256
257         list_add_tail(&resource->node, &res_pool->free_list);
258         res_pool->free_count++;
259 out:
260         if (mutex_locked)
261                 mutex_unlock(&res_pool->mutex);
262 }
263 EXPORT_SYMBOL_GPL(fsl_mc_resource_free);
264
265 /**
266  * fsl_mc_portal_allocate - Allocates an MC portal
267  *
268  * @mc_dev: MC device for which the MC portal is to be allocated
269  * @mc_io_flags: Flags for the fsl_mc_io object that wraps the allocated
270  * MC portal.
271  * @new_mc_io: Pointer to area where the pointer to the fsl_mc_io object
272  * that wraps the allocated MC portal is to be returned
273  *
274  * This function allocates an MC portal from the device's parent DPRC,
275  * from the corresponding MC bus' pool of MC portals and wraps
276  * it in a new fsl_mc_io object. If 'mc_dev' is a DPRC itself, the
277  * portal is allocated from its own MC bus.
278  */
279 int __must_check fsl_mc_portal_allocate(struct fsl_mc_device *mc_dev,
280                                         uint16_t mc_io_flags,
281                                         struct fsl_mc_io **new_mc_io)
282 {
283         struct fsl_mc_device *mc_bus_dev;
284         struct fsl_mc_bus *mc_bus;
285         phys_addr_t mc_portal_phys_addr;
286         size_t mc_portal_size;
287         struct fsl_mc_device *mc_adev;
288         int error = -EINVAL;
289         struct fsl_mc_resource *resource = NULL;
290         struct fsl_mc_io *mc_io = NULL;
291
292         if (mc_dev->flags & FSL_MC_IS_DPRC) {
293                 mc_bus_dev = mc_dev;
294         } else {
295                 if (WARN_ON(mc_dev->dev.parent->bus != &fsl_mc_bus_type))
296                         return error;
297
298                 mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
299         }
300
301         mc_bus = to_fsl_mc_bus(mc_bus_dev);
302         *new_mc_io = NULL;
303         error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_DPMCP, &resource);
304         if (error < 0)
305                 return error;
306
307         mc_adev = resource->data;
308         if (WARN_ON(!mc_adev))
309                 goto error_cleanup_resource;
310
311         if (WARN_ON(mc_adev->obj_desc.region_count == 0))
312                 goto error_cleanup_resource;
313
314         mc_portal_phys_addr = mc_adev->regions[0].start;
315         mc_portal_size = mc_adev->regions[0].end -
316                          mc_adev->regions[0].start + 1;
317
318         if (WARN_ON(mc_portal_size != mc_bus_dev->mc_io->portal_size))
319                 goto error_cleanup_resource;
320
321         error = fsl_create_mc_io(&mc_bus_dev->dev,
322                                  mc_portal_phys_addr,
323                                  mc_portal_size, resource,
324                                  mc_io_flags, &mc_io);
325         if (error < 0)
326                 goto error_cleanup_resource;
327
328         *new_mc_io = mc_io;
329         return 0;
330
331 error_cleanup_resource:
332         fsl_mc_resource_free(resource);
333         return error;
334 }
335 EXPORT_SYMBOL_GPL(fsl_mc_portal_allocate);
336
337 /**
338  * fsl_mc_portal_free - Returns an MC portal to the pool of free MC portals
339  * of a given MC bus
340  *
341  * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free
342  */
343 void fsl_mc_portal_free(struct fsl_mc_io *mc_io)
344 {
345         struct fsl_mc_resource *resource;
346
347         resource = mc_io->resource;
348         if (WARN_ON(resource->type != FSL_MC_POOL_DPMCP))
349                 return;
350         if (WARN_ON(!resource->data))
351                 return;
352
353         fsl_destroy_mc_io(mc_io);
354         fsl_mc_resource_free(resource);
355 }
356 EXPORT_SYMBOL_GPL(fsl_mc_portal_free);
357
358 /**
359  * fsl_mc_portal_reset - Resets the dpmcp object for a given fsl_mc_io object
360  *
361  * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free
362  */
363 int fsl_mc_portal_reset(struct fsl_mc_io *mc_io)
364 {
365         int error;
366         uint16_t token;
367         struct fsl_mc_resource *resource = mc_io->resource;
368         struct fsl_mc_device *mc_dev = resource->data;
369
370         if (WARN_ON(resource->type != FSL_MC_POOL_DPMCP))
371                 return -EINVAL;
372
373         if (WARN_ON(!mc_dev))
374                 return -EINVAL;
375
376         error = dpmcp_open(mc_io, mc_dev->obj_desc.id, &token);
377         if (error < 0) {
378                 dev_err(&mc_dev->dev, "dpmcp_open() failed: %d\n", error);
379                 return error;
380         }
381
382         error = dpmcp_reset(mc_io, token);
383         if (error < 0) {
384                 dev_err(&mc_dev->dev, "dpmcp_reset() failed: %d\n", error);
385                 return error;
386         }
387
388         error = dpmcp_close(mc_io, token);
389         if (error < 0) {
390                 dev_err(&mc_dev->dev, "dpmcp_close() failed: %d\n", error);
391                 return error;
392         }
393
394         return 0;
395 }
396 EXPORT_SYMBOL_GPL(fsl_mc_portal_reset);
397
398 /**
399  * fsl_mc_object_allocate - Allocates a MC object device of the given
400  * pool type from a given MC bus
401  *
402  * @mc_dev: MC device for which the MC object device is to be allocated
403  * @pool_type: MC bus resource pool type
404  * @new_mc_dev: Pointer to area where the pointer to the allocated
405  * MC object device is to be returned
406  *
407  * This function allocates a MC object device from the device's parent DPRC,
408  * from the corresponding MC bus' pool of allocatable MC object devices of
409  * the given resource type. mc_dev cannot be a DPRC itself.
410  *
411  * NOTE: pool_type must be different from FSL_MC_POOL_MCP, since MC
412  * portals are allocated using fsl_mc_portal_allocate(), instead of
413  * this function.
414  */
415 int __must_check fsl_mc_object_allocate(struct fsl_mc_device *mc_dev,
416                                         enum fsl_mc_pool_type pool_type,
417                                         struct fsl_mc_device **new_mc_adev)
418 {
419         struct fsl_mc_device *mc_bus_dev;
420         struct fsl_mc_bus *mc_bus;
421         struct fsl_mc_device *mc_adev;
422         int error = -EINVAL;
423         struct fsl_mc_resource *resource = NULL;
424
425         *new_mc_adev = NULL;
426         if (WARN_ON(mc_dev->flags & FSL_MC_IS_DPRC))
427                 goto error;
428
429         if (WARN_ON(mc_dev->dev.parent->bus != &fsl_mc_bus_type))
430                 goto error;
431
432         if (WARN_ON(pool_type == FSL_MC_POOL_DPMCP))
433                 goto error;
434
435         mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
436         mc_bus = to_fsl_mc_bus(mc_bus_dev);
437         error = fsl_mc_resource_allocate(mc_bus, pool_type, &resource);
438         if (error < 0)
439                 goto error;
440
441         mc_adev = resource->data;
442         if (WARN_ON(!mc_adev))
443                 goto error;
444
445         *new_mc_adev = mc_adev;
446         return 0;
447 error:
448         if (resource)
449                 fsl_mc_resource_free(resource);
450
451         return error;
452 }
453 EXPORT_SYMBOL_GPL(fsl_mc_object_allocate);
454
455 /**
456  * fsl_mc_object_free - Returns an allocatable MC object device to the
457  * corresponding resource pool of a given MC bus.
458  *
459  * @mc_adev: Pointer to the MC object device
460  */
461 void fsl_mc_object_free(struct fsl_mc_device *mc_adev)
462 {
463         struct fsl_mc_resource *resource;
464
465         resource = mc_adev->resource;
466         if (WARN_ON(resource->type == FSL_MC_POOL_DPMCP))
467                 return;
468         if (WARN_ON(resource->data != mc_adev))
469                 return;
470
471         fsl_mc_resource_free(resource);
472 }
473 EXPORT_SYMBOL_GPL(fsl_mc_object_free);
474
475 /**
476  * fsl_mc_allocator_probe - callback invoked when an allocatable device is
477  * being added to the system
478  */
479 static int fsl_mc_allocator_probe(struct fsl_mc_device *mc_dev)
480 {
481         enum fsl_mc_pool_type pool_type;
482         struct fsl_mc_device *mc_bus_dev;
483         struct fsl_mc_bus *mc_bus;
484         int error = -EINVAL;
485
486         if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
487                 goto error;
488
489         mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
490         if (WARN_ON(mc_bus_dev->dev.bus != &fsl_mc_bus_type))
491                 goto error;
492
493         mc_bus = to_fsl_mc_bus(mc_bus_dev);
494         error = object_type_to_pool_type(mc_dev->obj_desc.type, &pool_type);
495         if (error < 0)
496                 goto error;
497
498         error = fsl_mc_resource_pool_add_device(mc_bus, pool_type, mc_dev);
499         if (error < 0)
500                 goto error;
501
502         dev_info(&mc_dev->dev,
503                  "Allocatable MC object device bound to fsl_mc_allocator driver");
504         return 0;
505 error:
506
507         return error;
508 }
509
510 /**
511  * fsl_mc_allocator_remove - callback invoked when an allocatable device is
512  * being removed from the system
513  */
514 static int fsl_mc_allocator_remove(struct fsl_mc_device *mc_dev)
515 {
516         int error = -EINVAL;
517
518         if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
519                 goto out;
520
521         error = fsl_mc_resource_pool_remove_device(mc_dev);
522         if (error < 0)
523                 goto out;
524
525         dev_info(&mc_dev->dev,
526                  "Allocatable MC object device unbound from fsl_mc_allocator driver");
527         error = 0;
528 out:
529         return error;
530 }
531
532 static const struct fsl_mc_device_match_id match_id_table[] = {
533         {
534          .vendor = FSL_MC_VENDOR_FREESCALE,
535          .obj_type = "dpbp",
536          .ver_major = DPBP_VER_MAJOR,
537          .ver_minor = DPBP_VER_MINOR
538         },
539         {
540          .vendor = FSL_MC_VENDOR_FREESCALE,
541          .obj_type = "dpmcp",
542          .ver_major = DPMCP_VER_MAJOR,
543          .ver_minor = DPMCP_VER_MINOR
544         },
545         {
546          .vendor = FSL_MC_VENDOR_FREESCALE,
547          .obj_type = "dpcon",
548          .ver_major = DPCON_VER_MAJOR,
549          .ver_minor = DPCON_VER_MINOR
550         },
551         {.vendor = 0x0},
552 };
553
554 static struct fsl_mc_driver fsl_mc_allocator_driver = {
555         .driver = {
556                    .name = "fsl_mc_allocator",
557                    .owner = THIS_MODULE,
558                    .pm = NULL,
559                    },
560         .match_id_table = match_id_table,
561         .probe = fsl_mc_allocator_probe,
562         .remove = fsl_mc_allocator_remove,
563 };
564
565 int __init fsl_mc_allocator_driver_init(void)
566 {
567         return fsl_mc_driver_register(&fsl_mc_allocator_driver);
568 }
569
570 void __exit fsl_mc_allocator_driver_exit(void)
571 {
572         fsl_mc_driver_unregister(&fsl_mc_allocator_driver);
573 }