• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 || 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,u16 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 					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  */
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_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  */
fsl_mc_portal_reset(struct fsl_mc_io * mc_io)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  */
fsl_mc_object_allocate(struct fsl_mc_device * mc_dev,enum fsl_mc_pool_type pool_type,struct fsl_mc_device ** new_mc_adev)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  */
fsl_mc_object_free(struct fsl_mc_device * mc_adev)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  */
fsl_mc_allocator_probe(struct fsl_mc_device * mc_dev)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  */
fsl_mc_allocator_remove(struct fsl_mc_device * mc_dev)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 
fsl_mc_allocator_driver_init(void)555 int __init fsl_mc_allocator_driver_init(void)
556 {
557 	return fsl_mc_driver_register(&fsl_mc_allocator_driver);
558 }
559 
fsl_mc_allocator_driver_exit(void)560 void __exit fsl_mc_allocator_driver_exit(void)
561 {
562 	fsl_mc_driver_unregister(&fsl_mc_allocator_driver);
563 }
564