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  */
fsl_mc_resource_pool_add_device(struct fsl_mc_bus * mc_bus,enum fsl_mc_pool_type pool_type,struct fsl_mc_device * mc_dev)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  */
fsl_mc_resource_pool_remove_device(struct fsl_mc_device * mc_dev)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 
object_type_to_pool_type(const char * object_type,enum fsl_mc_pool_type * pool_type)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 
fsl_mc_resource_allocate(struct fsl_mc_bus * mc_bus,enum fsl_mc_pool_type pool_type,struct fsl_mc_resource ** new_resource)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 
fsl_mc_resource_free(struct fsl_mc_resource * resource)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  */
fsl_mc_portal_allocate(struct fsl_mc_device * mc_dev,uint16_t mc_io_flags,struct fsl_mc_io ** new_mc_io)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  */
fsl_mc_portal_free(struct fsl_mc_io * mc_io)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  */
fsl_mc_portal_reset(struct fsl_mc_io * mc_io)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  */
fsl_mc_object_allocate(struct fsl_mc_device * mc_dev,enum fsl_mc_pool_type pool_type,struct fsl_mc_device ** new_mc_adev)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  */
fsl_mc_object_free(struct fsl_mc_device * mc_adev)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  */
fsl_mc_allocator_probe(struct fsl_mc_device * mc_dev)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  */
fsl_mc_allocator_remove(struct fsl_mc_device * mc_dev)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 
fsl_mc_allocator_driver_init(void)565 int __init fsl_mc_allocator_driver_init(void)
566 {
567 	return fsl_mc_driver_register(&fsl_mc_allocator_driver);
568 }
569 
fsl_mc_allocator_driver_exit(void)570 void __exit fsl_mc_allocator_driver_exit(void)
571 {
572 	fsl_mc_driver_unregister(&fsl_mc_allocator_driver);
573 }
574