These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[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 || 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                                         u16 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 *dpmcp_dev;
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         dpmcp_dev = resource->data;
308         if (WARN_ON(!dpmcp_dev))
309                 goto error_cleanup_resource;
310
311         if (WARN_ON(dpmcp_dev->obj_desc.region_count == 0))
312                 goto error_cleanup_resource;
313
314         mc_portal_phys_addr = dpmcp_dev->regions[0].start;
315         mc_portal_size = dpmcp_dev->regions[0].end -
316                          dpmcp_dev->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, dpmcp_dev,
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_device *dpmcp_dev;
346         struct fsl_mc_resource *resource;
347
348         /*
349          * Every mc_io obtained by calling fsl_mc_portal_allocate() is supposed
350          * to have a DPMCP object associated with.
351          */
352         dpmcp_dev = mc_io->dpmcp_dev;
353         if (WARN_ON(!dpmcp_dev))
354                 return;
355
356         resource = dpmcp_dev->resource;
357         if (WARN_ON(!resource || resource->type != FSL_MC_POOL_DPMCP))
358                 return;
359
360         if (WARN_ON(resource->data != dpmcp_dev))
361                 return;
362
363         fsl_destroy_mc_io(mc_io);
364         fsl_mc_resource_free(resource);
365 }
366 EXPORT_SYMBOL_GPL(fsl_mc_portal_free);
367
368 /**
369  * fsl_mc_portal_reset - Resets the dpmcp object for a given fsl_mc_io object
370  *
371  * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free
372  */
373 int fsl_mc_portal_reset(struct fsl_mc_io *mc_io)
374 {
375         int error;
376         struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev;
377
378         if (WARN_ON(!dpmcp_dev))
379                 return -EINVAL;
380
381         error = dpmcp_reset(mc_io, 0, dpmcp_dev->mc_handle);
382         if (error < 0) {
383                 dev_err(&dpmcp_dev->dev, "dpmcp_reset() failed: %d\n", error);
384                 return error;
385         }
386
387         return 0;
388 }
389 EXPORT_SYMBOL_GPL(fsl_mc_portal_reset);
390
391 /**
392  * fsl_mc_object_allocate - Allocates a MC object device of the given
393  * pool type from a given MC bus
394  *
395  * @mc_dev: MC device for which the MC object device is to be allocated
396  * @pool_type: MC bus resource pool type
397  * @new_mc_dev: Pointer to area where the pointer to the allocated
398  * MC object device is to be returned
399  *
400  * This function allocates a MC object device from the device's parent DPRC,
401  * from the corresponding MC bus' pool of allocatable MC object devices of
402  * the given resource type. mc_dev cannot be a DPRC itself.
403  *
404  * NOTE: pool_type must be different from FSL_MC_POOL_MCP, since MC
405  * portals are allocated using fsl_mc_portal_allocate(), instead of
406  * this function.
407  */
408 int __must_check fsl_mc_object_allocate(struct fsl_mc_device *mc_dev,
409                                         enum fsl_mc_pool_type pool_type,
410                                         struct fsl_mc_device **new_mc_adev)
411 {
412         struct fsl_mc_device *mc_bus_dev;
413         struct fsl_mc_bus *mc_bus;
414         struct fsl_mc_device *mc_adev;
415         int error = -EINVAL;
416         struct fsl_mc_resource *resource = NULL;
417
418         *new_mc_adev = NULL;
419         if (WARN_ON(mc_dev->flags & FSL_MC_IS_DPRC))
420                 goto error;
421
422         if (WARN_ON(mc_dev->dev.parent->bus != &fsl_mc_bus_type))
423                 goto error;
424
425         if (WARN_ON(pool_type == FSL_MC_POOL_DPMCP))
426                 goto error;
427
428         mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
429         mc_bus = to_fsl_mc_bus(mc_bus_dev);
430         error = fsl_mc_resource_allocate(mc_bus, pool_type, &resource);
431         if (error < 0)
432                 goto error;
433
434         mc_adev = resource->data;
435         if (WARN_ON(!mc_adev))
436                 goto error;
437
438         *new_mc_adev = mc_adev;
439         return 0;
440 error:
441         if (resource)
442                 fsl_mc_resource_free(resource);
443
444         return error;
445 }
446 EXPORT_SYMBOL_GPL(fsl_mc_object_allocate);
447
448 /**
449  * fsl_mc_object_free - Returns an allocatable MC object device to the
450  * corresponding resource pool of a given MC bus.
451  *
452  * @mc_adev: Pointer to the MC object device
453  */
454 void fsl_mc_object_free(struct fsl_mc_device *mc_adev)
455 {
456         struct fsl_mc_resource *resource;
457
458         resource = mc_adev->resource;
459         if (WARN_ON(resource->type == FSL_MC_POOL_DPMCP))
460                 return;
461         if (WARN_ON(resource->data != mc_adev))
462                 return;
463
464         fsl_mc_resource_free(resource);
465 }
466 EXPORT_SYMBOL_GPL(fsl_mc_object_free);
467
468 /**
469  * fsl_mc_allocator_probe - callback invoked when an allocatable device is
470  * being added to the system
471  */
472 static int fsl_mc_allocator_probe(struct fsl_mc_device *mc_dev)
473 {
474         enum fsl_mc_pool_type pool_type;
475         struct fsl_mc_device *mc_bus_dev;
476         struct fsl_mc_bus *mc_bus;
477         int error;
478
479         if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
480                 return -EINVAL;
481
482         mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
483         if (WARN_ON(mc_bus_dev->dev.bus != &fsl_mc_bus_type))
484                 return -EINVAL;
485
486         mc_bus = to_fsl_mc_bus(mc_bus_dev);
487         error = object_type_to_pool_type(mc_dev->obj_desc.type, &pool_type);
488         if (error < 0)
489                 return error;
490
491         error = fsl_mc_resource_pool_add_device(mc_bus, pool_type, mc_dev);
492         if (error < 0)
493                 return error;
494
495         dev_dbg(&mc_dev->dev,
496                 "Allocatable MC object device bound to fsl_mc_allocator driver");
497         return 0;
498 }
499
500 /**
501  * fsl_mc_allocator_remove - callback invoked when an allocatable device is
502  * being removed from the system
503  */
504 static int fsl_mc_allocator_remove(struct fsl_mc_device *mc_dev)
505 {
506         int error;
507
508         if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
509                 return -EINVAL;
510
511         if (mc_dev->resource) {
512                 error = fsl_mc_resource_pool_remove_device(mc_dev);
513                 if (error < 0)
514                         return error;
515         }
516
517         dev_dbg(&mc_dev->dev,
518                 "Allocatable MC object device unbound from fsl_mc_allocator driver");
519         return 0;
520 }
521
522 static const struct fsl_mc_device_match_id match_id_table[] = {
523         {
524          .vendor = FSL_MC_VENDOR_FREESCALE,
525          .obj_type = "dpbp",
526          .ver_major = DPBP_VER_MAJOR,
527          .ver_minor = DPBP_VER_MINOR
528         },
529         {
530          .vendor = FSL_MC_VENDOR_FREESCALE,
531          .obj_type = "dpmcp",
532          .ver_major = DPMCP_VER_MAJOR,
533          .ver_minor = DPMCP_VER_MINOR
534         },
535         {
536          .vendor = FSL_MC_VENDOR_FREESCALE,
537          .obj_type = "dpcon",
538          .ver_major = DPCON_VER_MAJOR,
539          .ver_minor = DPCON_VER_MINOR
540         },
541         {.vendor = 0x0},
542 };
543
544 static struct fsl_mc_driver fsl_mc_allocator_driver = {
545         .driver = {
546                    .name = "fsl_mc_allocator",
547                    .owner = THIS_MODULE,
548                    .pm = NULL,
549                    },
550         .match_id_table = match_id_table,
551         .probe = fsl_mc_allocator_probe,
552         .remove = fsl_mc_allocator_remove,
553 };
554
555 int __init fsl_mc_allocator_driver_init(void)
556 {
557         return fsl_mc_driver_register(&fsl_mc_allocator_driver);
558 }
559
560 void __exit fsl_mc_allocator_driver_exit(void)
561 {
562         fsl_mc_driver_unregister(&fsl_mc_allocator_driver);
563 }