blob: 88d1857521a530c07e50c626b91a67f0dc86f315 [file] [log] [blame]
J. German Rivera197f4d62015-03-05 19:35:25 -06001/*
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 */
30static 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;
84out:
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 */
100static 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;
J. German Rivera2379bdb2015-10-17 11:18:22 -0500114 if (WARN_ON(!resource || resource->data != mc_dev))
J. German Rivera197f4d62015-03-05 19:35:25 -0600115 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;
152out:
153 if (mutex_locked)
154 mutex_unlock(&res_pool->mutex);
155
156 return error;
157}
158
159static 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
165static 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
181int __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;
231error:
232 if (mutex_locked)
233 mutex_unlock(&res_pool->mutex);
234
235 return error;
236}
237EXPORT_SYMBOL_GPL(fsl_mc_resource_allocate);
238
239void 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++;
259out:
260 if (mutex_locked)
261 mutex_unlock(&res_pool->mutex);
262}
263EXPORT_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 */
279int __must_check fsl_mc_portal_allocate(struct fsl_mc_device *mc_dev,
J. German Riveraba72f252015-09-25 11:21:01 -0500280 u16 mc_io_flags,
J. German Rivera197f4d62015-03-05 19:35:25 -0600281 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;
J. German Rivera724e50f2015-10-17 11:18:13 -0500287 struct fsl_mc_device *dpmcp_dev;
J. German Rivera197f4d62015-03-05 19:35:25 -0600288 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
J. German Rivera724e50f2015-10-17 11:18:13 -0500307 dpmcp_dev = resource->data;
308 if (WARN_ON(!dpmcp_dev))
J. German Rivera197f4d62015-03-05 19:35:25 -0600309 goto error_cleanup_resource;
310
J. German Rivera724e50f2015-10-17 11:18:13 -0500311 if (WARN_ON(dpmcp_dev->obj_desc.region_count == 0))
J. German Rivera197f4d62015-03-05 19:35:25 -0600312 goto error_cleanup_resource;
313
J. German Rivera724e50f2015-10-17 11:18:13 -0500314 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;
J. German Rivera197f4d62015-03-05 19:35:25 -0600317
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,
J. German Riveraffcd52e2015-10-17 11:18:14 -0500323 mc_portal_size, dpmcp_dev,
J. German Rivera197f4d62015-03-05 19:35:25 -0600324 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
331error_cleanup_resource:
332 fsl_mc_resource_free(resource);
333 return error;
334}
335EXPORT_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 */
343void fsl_mc_portal_free(struct fsl_mc_io *mc_io)
344{
J. German Riveraffcd52e2015-10-17 11:18:14 -0500345 struct fsl_mc_device *dpmcp_dev;
J. German Rivera197f4d62015-03-05 19:35:25 -0600346 struct fsl_mc_resource *resource;
347
J. German Riveraffcd52e2015-10-17 11:18:14 -0500348 /*
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))
J. German Rivera197f4d62015-03-05 19:35:25 -0600354 return;
J. German Riveraffcd52e2015-10-17 11:18:14 -0500355
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))
J. German Rivera197f4d62015-03-05 19:35:25 -0600361 return;
362
363 fsl_destroy_mc_io(mc_io);
364 fsl_mc_resource_free(resource);
365}
366EXPORT_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 */
373int fsl_mc_portal_reset(struct fsl_mc_io *mc_io)
374{
375 int error;
J. German Riveraffcd52e2015-10-17 11:18:14 -0500376 struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev;
J. German Rivera197f4d62015-03-05 19:35:25 -0600377
J. German Riveraffcd52e2015-10-17 11:18:14 -0500378 if (WARN_ON(!dpmcp_dev))
J. German Rivera197f4d62015-03-05 19:35:25 -0600379 return -EINVAL;
380
J. German Riverad2f84992015-10-17 11:18:15 -0500381 error = dpmcp_reset(mc_io, 0, dpmcp_dev->mc_handle);
J. German Rivera197f4d62015-03-05 19:35:25 -0600382 if (error < 0) {
J. German Riveraffcd52e2015-10-17 11:18:14 -0500383 dev_err(&dpmcp_dev->dev, "dpmcp_reset() failed: %d\n", error);
J. German Rivera197f4d62015-03-05 19:35:25 -0600384 return error;
385 }
386
J. German Rivera197f4d62015-03-05 19:35:25 -0600387 return 0;
388}
389EXPORT_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 */
408int __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;
440error:
441 if (resource)
442 fsl_mc_resource_free(resource);
443
444 return error;
445}
446EXPORT_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 */
454void 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}
466EXPORT_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 */
472static 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;
J. German Rivera09a38ab2015-10-17 11:18:21 -0500477 int error;
J. German Rivera197f4d62015-03-05 19:35:25 -0600478
479 if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
J. German Rivera09a38ab2015-10-17 11:18:21 -0500480 return -EINVAL;
J. German Rivera197f4d62015-03-05 19:35:25 -0600481
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))
J. German Rivera09a38ab2015-10-17 11:18:21 -0500484 return -EINVAL;
J. German Rivera197f4d62015-03-05 19:35:25 -0600485
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)
J. German Rivera09a38ab2015-10-17 11:18:21 -0500489 return error;
J. German Rivera197f4d62015-03-05 19:35:25 -0600490
491 error = fsl_mc_resource_pool_add_device(mc_bus, pool_type, mc_dev);
492 if (error < 0)
J. German Rivera09a38ab2015-10-17 11:18:21 -0500493 return error;
J. German Rivera197f4d62015-03-05 19:35:25 -0600494
J. German Rivera6998d6b2015-10-17 11:18:16 -0500495 dev_dbg(&mc_dev->dev,
496 "Allocatable MC object device bound to fsl_mc_allocator driver");
J. German Rivera197f4d62015-03-05 19:35:25 -0600497 return 0;
J. German Rivera197f4d62015-03-05 19:35:25 -0600498}
499
500/**
501 * fsl_mc_allocator_remove - callback invoked when an allocatable device is
502 * being removed from the system
503 */
504static int fsl_mc_allocator_remove(struct fsl_mc_device *mc_dev)
505{
J. German Rivera09a38ab2015-10-17 11:18:21 -0500506 int error;
J. German Rivera197f4d62015-03-05 19:35:25 -0600507
508 if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
J. German Rivera09a38ab2015-10-17 11:18:21 -0500509 return -EINVAL;
J. German Rivera197f4d62015-03-05 19:35:25 -0600510
J. German Rivera6958cd42015-10-17 11:18:20 -0500511 if (mc_dev->resource) {
512 error = fsl_mc_resource_pool_remove_device(mc_dev);
513 if (error < 0)
J. German Rivera09a38ab2015-10-17 11:18:21 -0500514 return error;
J. German Rivera6958cd42015-10-17 11:18:20 -0500515 }
J. German Rivera197f4d62015-03-05 19:35:25 -0600516
J. German Rivera6998d6b2015-10-17 11:18:16 -0500517 dev_dbg(&mc_dev->dev,
518 "Allocatable MC object device unbound from fsl_mc_allocator driver");
J. German Rivera09a38ab2015-10-17 11:18:21 -0500519 return 0;
J. German Rivera197f4d62015-03-05 19:35:25 -0600520}
521
522static 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
544static 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
J. German Riverae91ffa92015-03-27 16:01:08 -0500555int __init fsl_mc_allocator_driver_init(void)
556{
557 return fsl_mc_driver_register(&fsl_mc_allocator_driver);
558}
J. German Rivera197f4d62015-03-05 19:35:25 -0600559
J. German Riverae91ffa92015-03-27 16:01:08 -0500560void __exit fsl_mc_allocator_driver_exit(void)
561{
562 fsl_mc_driver_unregister(&fsl_mc_allocator_driver);
563}