Commit | Line | Data |
---|---|---|
197f4d6a GR |
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 | ||
d4e75132 SY |
11 | #include <linux/module.h> |
12 | #include <linux/msi.h> | |
c37ebf8c | 13 | #include "../include/mc-bus.h" |
197f4d6a | 14 | #include "../include/mc-sys.h" |
197f4d6a GR |
15 | #include "../include/dpbp-cmd.h" |
16 | #include "../include/dpcon-cmd.h" | |
d4e75132 | 17 | |
243444fb | 18 | #include "fsl-mc-private.h" |
197f4d6a | 19 | |
e267dddd SY |
20 | #define FSL_MC_IS_ALLOCATABLE(_obj_type) \ |
21 | (strcmp(_obj_type, "dpbp") == 0 || \ | |
22 | strcmp(_obj_type, "dpmcp") == 0 || \ | |
23 | strcmp(_obj_type, "dpcon") == 0) | |
24 | ||
197f4d6a GR |
25 | /** |
26 | * fsl_mc_resource_pool_add_device - add allocatable device to a resource | |
27 | * pool of a given MC bus | |
28 | * | |
29 | * @mc_bus: pointer to the MC bus | |
30 | * @pool_type: MC bus pool type | |
31 | * @mc_dev: Pointer to allocatable MC object device | |
32 | * | |
33 | * It adds an allocatable MC object device to a container's resource pool of | |
34 | * the given resource type | |
35 | */ | |
36 | static int __must_check fsl_mc_resource_pool_add_device(struct fsl_mc_bus | |
37 | *mc_bus, | |
38 | enum fsl_mc_pool_type | |
39 | pool_type, | |
40 | struct fsl_mc_device | |
41 | *mc_dev) | |
42 | { | |
43 | struct fsl_mc_resource_pool *res_pool; | |
44 | struct fsl_mc_resource *resource; | |
45 | struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; | |
46 | int error = -EINVAL; | |
197f4d6a GR |
47 | |
48 | if (WARN_ON(pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES)) | |
49 | goto out; | |
50 | if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type))) | |
51 | goto out; | |
52 | if (WARN_ON(mc_dev->resource)) | |
53 | goto out; | |
54 | ||
55 | res_pool = &mc_bus->resource_pools[pool_type]; | |
56 | if (WARN_ON(res_pool->type != pool_type)) | |
57 | goto out; | |
58 | if (WARN_ON(res_pool->mc_bus != mc_bus)) | |
59 | goto out; | |
60 | ||
61 | mutex_lock(&res_pool->mutex); | |
197f4d6a GR |
62 | |
63 | if (WARN_ON(res_pool->max_count < 0)) | |
6ffdc7b9 | 64 | goto out_unlock; |
197f4d6a GR |
65 | if (WARN_ON(res_pool->free_count < 0 || |
66 | res_pool->free_count > res_pool->max_count)) | |
6ffdc7b9 | 67 | goto out_unlock; |
197f4d6a GR |
68 | |
69 | resource = devm_kzalloc(&mc_bus_dev->dev, sizeof(*resource), | |
70 | GFP_KERNEL); | |
71 | if (!resource) { | |
72 | error = -ENOMEM; | |
73 | dev_err(&mc_bus_dev->dev, | |
74 | "Failed to allocate memory for fsl_mc_resource\n"); | |
6ffdc7b9 | 75 | goto out_unlock; |
197f4d6a GR |
76 | } |
77 | ||
78 | resource->type = pool_type; | |
79 | resource->id = mc_dev->obj_desc.id; | |
80 | resource->data = mc_dev; | |
81 | resource->parent_pool = res_pool; | |
82 | INIT_LIST_HEAD(&resource->node); | |
83 | list_add_tail(&resource->node, &res_pool->free_list); | |
84 | mc_dev->resource = resource; | |
85 | res_pool->free_count++; | |
86 | res_pool->max_count++; | |
87 | error = 0; | |
6ffdc7b9 CA |
88 | out_unlock: |
89 | mutex_unlock(&res_pool->mutex); | |
197f4d6a | 90 | out: |
197f4d6a GR |
91 | return error; |
92 | } | |
93 | ||
94 | /** | |
95 | * fsl_mc_resource_pool_remove_device - remove an allocatable device from a | |
96 | * resource pool | |
97 | * | |
98 | * @mc_dev: Pointer to allocatable MC object device | |
99 | * | |
100 | * It permanently removes an allocatable MC object device from the resource | |
101 | * pool, the device is currently in, as long as it is in the pool's free list. | |
102 | */ | |
103 | static int __must_check fsl_mc_resource_pool_remove_device(struct fsl_mc_device | |
104 | *mc_dev) | |
105 | { | |
106 | struct fsl_mc_device *mc_bus_dev; | |
107 | struct fsl_mc_bus *mc_bus; | |
108 | struct fsl_mc_resource_pool *res_pool; | |
109 | struct fsl_mc_resource *resource; | |
110 | int error = -EINVAL; | |
197f4d6a GR |
111 | |
112 | if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type))) | |
113 | goto out; | |
114 | ||
115 | resource = mc_dev->resource; | |
2379bdbc | 116 | if (WARN_ON(!resource || resource->data != mc_dev)) |
197f4d6a GR |
117 | goto out; |
118 | ||
119 | mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); | |
120 | mc_bus = to_fsl_mc_bus(mc_bus_dev); | |
121 | res_pool = resource->parent_pool; | |
122 | if (WARN_ON(res_pool != &mc_bus->resource_pools[resource->type])) | |
123 | goto out; | |
124 | ||
125 | mutex_lock(&res_pool->mutex); | |
197f4d6a GR |
126 | |
127 | if (WARN_ON(res_pool->max_count <= 0)) | |
6ffdc7b9 | 128 | goto out_unlock; |
197f4d6a GR |
129 | if (WARN_ON(res_pool->free_count <= 0 || |
130 | res_pool->free_count > res_pool->max_count)) | |
6ffdc7b9 | 131 | goto out_unlock; |
197f4d6a GR |
132 | |
133 | /* | |
134 | * If the device is currently allocated, its resource is not | |
135 | * in the free list and thus, the device cannot be removed. | |
136 | */ | |
137 | if (list_empty(&resource->node)) { | |
138 | error = -EBUSY; | |
139 | dev_err(&mc_bus_dev->dev, | |
140 | "Device %s cannot be removed from resource pool\n", | |
141 | dev_name(&mc_dev->dev)); | |
6ffdc7b9 | 142 | goto out_unlock; |
197f4d6a GR |
143 | } |
144 | ||
145 | list_del(&resource->node); | |
146 | INIT_LIST_HEAD(&resource->node); | |
147 | res_pool->free_count--; | |
148 | res_pool->max_count--; | |
149 | ||
150 | devm_kfree(&mc_bus_dev->dev, resource); | |
151 | mc_dev->resource = NULL; | |
152 | error = 0; | |
6ffdc7b9 CA |
153 | out_unlock: |
154 | mutex_unlock(&res_pool->mutex); | |
197f4d6a | 155 | out: |
197f4d6a GR |
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", | |
89f067df | 163 | [FSL_MC_POOL_IRQ] = "irq", |
197f4d6a GR |
164 | }; |
165 | ||
166 | static int __must_check object_type_to_pool_type(const char *object_type, | |
167 | enum fsl_mc_pool_type | |
168 | *pool_type) | |
169 | { | |
170 | unsigned int i; | |
171 | ||
172 | for (i = 0; i < ARRAY_SIZE(fsl_mc_pool_type_strings); i++) { | |
173 | if (strcmp(object_type, fsl_mc_pool_type_strings[i]) == 0) { | |
174 | *pool_type = i; | |
175 | return 0; | |
176 | } | |
177 | } | |
178 | ||
179 | return -EINVAL; | |
180 | } | |
181 | ||
182 | int __must_check fsl_mc_resource_allocate(struct fsl_mc_bus *mc_bus, | |
183 | enum fsl_mc_pool_type pool_type, | |
184 | struct fsl_mc_resource **new_resource) | |
185 | { | |
186 | struct fsl_mc_resource_pool *res_pool; | |
187 | struct fsl_mc_resource *resource; | |
188 | struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; | |
189 | int error = -EINVAL; | |
197f4d6a GR |
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)) | |
6ffdc7b9 | 196 | goto out; |
197f4d6a GR |
197 | |
198 | res_pool = &mc_bus->resource_pools[pool_type]; | |
199 | if (WARN_ON(res_pool->mc_bus != mc_bus)) | |
6ffdc7b9 | 200 | goto out; |
197f4d6a GR |
201 | |
202 | mutex_lock(&res_pool->mutex); | |
197f4d6a GR |
203 | resource = list_first_entry_or_null(&res_pool->free_list, |
204 | struct fsl_mc_resource, node); | |
205 | ||
206 | if (!resource) { | |
207 | WARN_ON(res_pool->free_count != 0); | |
208 | error = -ENXIO; | |
209 | dev_err(&mc_bus_dev->dev, | |
210 | "No more resources of type %s left\n", | |
211 | fsl_mc_pool_type_strings[pool_type]); | |
6ffdc7b9 | 212 | goto out_unlock; |
197f4d6a GR |
213 | } |
214 | ||
215 | if (WARN_ON(resource->type != pool_type)) | |
6ffdc7b9 | 216 | goto out_unlock; |
197f4d6a | 217 | if (WARN_ON(resource->parent_pool != res_pool)) |
6ffdc7b9 | 218 | goto out_unlock; |
197f4d6a GR |
219 | if (WARN_ON(res_pool->free_count <= 0 || |
220 | res_pool->free_count > res_pool->max_count)) | |
6ffdc7b9 | 221 | goto out_unlock; |
197f4d6a GR |
222 | |
223 | list_del(&resource->node); | |
224 | INIT_LIST_HEAD(&resource->node); | |
225 | ||
226 | res_pool->free_count--; | |
6ffdc7b9 CA |
227 | error = 0; |
228 | out_unlock: | |
197f4d6a GR |
229 | mutex_unlock(&res_pool->mutex); |
230 | *new_resource = resource; | |
6ffdc7b9 | 231 | out: |
197f4d6a GR |
232 | return error; |
233 | } | |
234 | EXPORT_SYMBOL_GPL(fsl_mc_resource_allocate); | |
235 | ||
236 | void fsl_mc_resource_free(struct fsl_mc_resource *resource) | |
237 | { | |
238 | struct fsl_mc_resource_pool *res_pool; | |
197f4d6a GR |
239 | |
240 | res_pool = resource->parent_pool; | |
241 | if (WARN_ON(resource->type != res_pool->type)) | |
6ffdc7b9 | 242 | return; |
197f4d6a GR |
243 | |
244 | mutex_lock(&res_pool->mutex); | |
197f4d6a GR |
245 | if (WARN_ON(res_pool->free_count < 0 || |
246 | res_pool->free_count >= res_pool->max_count)) | |
6ffdc7b9 | 247 | goto out_unlock; |
197f4d6a GR |
248 | |
249 | if (WARN_ON(!list_empty(&resource->node))) | |
6ffdc7b9 | 250 | goto out_unlock; |
197f4d6a GR |
251 | |
252 | list_add_tail(&resource->node, &res_pool->free_list); | |
253 | res_pool->free_count++; | |
6ffdc7b9 CA |
254 | out_unlock: |
255 | mutex_unlock(&res_pool->mutex); | |
197f4d6a GR |
256 | } |
257 | EXPORT_SYMBOL_GPL(fsl_mc_resource_free); | |
258 | ||
197f4d6a GR |
259 | /** |
260 | * fsl_mc_object_allocate - Allocates a MC object device of the given | |
261 | * pool type from a given MC bus | |
262 | * | |
263 | * @mc_dev: MC device for which the MC object device is to be allocated | |
264 | * @pool_type: MC bus resource pool type | |
265 | * @new_mc_dev: Pointer to area where the pointer to the allocated | |
266 | * MC object device is to be returned | |
267 | * | |
268 | * This function allocates a MC object device from the device's parent DPRC, | |
269 | * from the corresponding MC bus' pool of allocatable MC object devices of | |
270 | * the given resource type. mc_dev cannot be a DPRC itself. | |
271 | * | |
272 | * NOTE: pool_type must be different from FSL_MC_POOL_MCP, since MC | |
273 | * portals are allocated using fsl_mc_portal_allocate(), instead of | |
274 | * this function. | |
275 | */ | |
276 | int __must_check fsl_mc_object_allocate(struct fsl_mc_device *mc_dev, | |
277 | enum fsl_mc_pool_type pool_type, | |
278 | struct fsl_mc_device **new_mc_adev) | |
279 | { | |
280 | struct fsl_mc_device *mc_bus_dev; | |
281 | struct fsl_mc_bus *mc_bus; | |
282 | struct fsl_mc_device *mc_adev; | |
283 | int error = -EINVAL; | |
284 | struct fsl_mc_resource *resource = NULL; | |
285 | ||
286 | *new_mc_adev = NULL; | |
287 | if (WARN_ON(mc_dev->flags & FSL_MC_IS_DPRC)) | |
288 | goto error; | |
289 | ||
df5e9b5f | 290 | if (WARN_ON(!dev_is_fsl_mc(mc_dev->dev.parent))) |
197f4d6a GR |
291 | goto error; |
292 | ||
293 | if (WARN_ON(pool_type == FSL_MC_POOL_DPMCP)) | |
294 | goto error; | |
295 | ||
296 | mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); | |
297 | mc_bus = to_fsl_mc_bus(mc_bus_dev); | |
298 | error = fsl_mc_resource_allocate(mc_bus, pool_type, &resource); | |
299 | if (error < 0) | |
300 | goto error; | |
301 | ||
302 | mc_adev = resource->data; | |
303 | if (WARN_ON(!mc_adev)) | |
304 | goto error; | |
305 | ||
306 | *new_mc_adev = mc_adev; | |
307 | return 0; | |
308 | error: | |
309 | if (resource) | |
310 | fsl_mc_resource_free(resource); | |
311 | ||
312 | return error; | |
313 | } | |
314 | EXPORT_SYMBOL_GPL(fsl_mc_object_allocate); | |
315 | ||
316 | /** | |
317 | * fsl_mc_object_free - Returns an allocatable MC object device to the | |
318 | * corresponding resource pool of a given MC bus. | |
319 | * | |
320 | * @mc_adev: Pointer to the MC object device | |
321 | */ | |
322 | void fsl_mc_object_free(struct fsl_mc_device *mc_adev) | |
323 | { | |
324 | struct fsl_mc_resource *resource; | |
325 | ||
326 | resource = mc_adev->resource; | |
327 | if (WARN_ON(resource->type == FSL_MC_POOL_DPMCP)) | |
328 | return; | |
329 | if (WARN_ON(resource->data != mc_adev)) | |
330 | return; | |
331 | ||
332 | fsl_mc_resource_free(resource); | |
333 | } | |
334 | EXPORT_SYMBOL_GPL(fsl_mc_object_free); | |
335 | ||
89f067df GR |
336 | /* |
337 | * Initialize the interrupt pool associated with a MC bus. | |
338 | * It allocates a block of IRQs from the GIC-ITS | |
339 | */ | |
340 | int fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus, | |
341 | unsigned int irq_count) | |
342 | { | |
343 | unsigned int i; | |
344 | struct msi_desc *msi_desc; | |
345 | struct fsl_mc_device_irq *irq_resources; | |
346 | struct fsl_mc_device_irq *mc_dev_irq; | |
347 | int error; | |
348 | struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; | |
349 | struct fsl_mc_resource_pool *res_pool = | |
350 | &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; | |
351 | ||
352 | if (WARN_ON(irq_count == 0 || | |
353 | irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS)) | |
354 | return -EINVAL; | |
355 | ||
356 | error = fsl_mc_msi_domain_alloc_irqs(&mc_bus_dev->dev, irq_count); | |
357 | if (error < 0) | |
358 | return error; | |
359 | ||
360 | irq_resources = devm_kzalloc(&mc_bus_dev->dev, | |
361 | sizeof(*irq_resources) * irq_count, | |
362 | GFP_KERNEL); | |
363 | if (!irq_resources) { | |
364 | error = -ENOMEM; | |
365 | goto cleanup_msi_irqs; | |
366 | } | |
367 | ||
368 | for (i = 0; i < irq_count; i++) { | |
369 | mc_dev_irq = &irq_resources[i]; | |
370 | ||
371 | /* | |
372 | * NOTE: This mc_dev_irq's MSI addr/value pair will be set | |
373 | * by the fsl_mc_msi_write_msg() callback | |
374 | */ | |
375 | mc_dev_irq->resource.type = res_pool->type; | |
376 | mc_dev_irq->resource.data = mc_dev_irq; | |
377 | mc_dev_irq->resource.parent_pool = res_pool; | |
378 | INIT_LIST_HEAD(&mc_dev_irq->resource.node); | |
379 | list_add_tail(&mc_dev_irq->resource.node, &res_pool->free_list); | |
380 | } | |
381 | ||
382 | for_each_msi_entry(msi_desc, &mc_bus_dev->dev) { | |
383 | mc_dev_irq = &irq_resources[msi_desc->fsl_mc.msi_index]; | |
384 | mc_dev_irq->msi_desc = msi_desc; | |
385 | mc_dev_irq->resource.id = msi_desc->irq; | |
386 | } | |
387 | ||
388 | res_pool->max_count = irq_count; | |
389 | res_pool->free_count = irq_count; | |
390 | mc_bus->irq_resources = irq_resources; | |
391 | return 0; | |
392 | ||
393 | cleanup_msi_irqs: | |
394 | fsl_mc_msi_domain_free_irqs(&mc_bus_dev->dev); | |
395 | return error; | |
396 | } | |
397 | EXPORT_SYMBOL_GPL(fsl_mc_populate_irq_pool); | |
398 | ||
399 | /** | |
400 | * Teardown the interrupt pool associated with an MC bus. | |
401 | * It frees the IRQs that were allocated to the pool, back to the GIC-ITS. | |
402 | */ | |
403 | void fsl_mc_cleanup_irq_pool(struct fsl_mc_bus *mc_bus) | |
404 | { | |
405 | struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; | |
406 | struct fsl_mc_resource_pool *res_pool = | |
407 | &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; | |
408 | ||
409 | if (WARN_ON(!mc_bus->irq_resources)) | |
410 | return; | |
411 | ||
412 | if (WARN_ON(res_pool->max_count == 0)) | |
413 | return; | |
414 | ||
415 | if (WARN_ON(res_pool->free_count != res_pool->max_count)) | |
416 | return; | |
417 | ||
418 | INIT_LIST_HEAD(&res_pool->free_list); | |
419 | res_pool->max_count = 0; | |
420 | res_pool->free_count = 0; | |
421 | mc_bus->irq_resources = NULL; | |
422 | fsl_mc_msi_domain_free_irqs(&mc_bus_dev->dev); | |
423 | } | |
424 | EXPORT_SYMBOL_GPL(fsl_mc_cleanup_irq_pool); | |
425 | ||
426 | /** | |
427 | * It allocates the IRQs required by a given MC object device. The | |
428 | * IRQs are allocated from the interrupt pool associated with the | |
429 | * MC bus that contains the device, if the device is not a DPRC device. | |
430 | * Otherwise, the IRQs are allocated from the interrupt pool associated | |
431 | * with the MC bus that represents the DPRC device itself. | |
432 | */ | |
433 | int __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev) | |
434 | { | |
435 | int i; | |
436 | int irq_count; | |
437 | int res_allocated_count = 0; | |
438 | int error = -EINVAL; | |
439 | struct fsl_mc_device_irq **irqs = NULL; | |
440 | struct fsl_mc_bus *mc_bus; | |
441 | struct fsl_mc_resource_pool *res_pool; | |
442 | ||
443 | if (WARN_ON(mc_dev->irqs)) | |
444 | return -EINVAL; | |
445 | ||
446 | irq_count = mc_dev->obj_desc.irq_count; | |
447 | if (WARN_ON(irq_count == 0)) | |
448 | return -EINVAL; | |
449 | ||
450 | if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) | |
451 | mc_bus = to_fsl_mc_bus(mc_dev); | |
452 | else | |
453 | mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent)); | |
454 | ||
455 | if (WARN_ON(!mc_bus->irq_resources)) | |
456 | return -EINVAL; | |
457 | ||
458 | res_pool = &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; | |
459 | if (res_pool->free_count < irq_count) { | |
460 | dev_err(&mc_dev->dev, | |
461 | "Not able to allocate %u irqs for device\n", irq_count); | |
462 | return -ENOSPC; | |
463 | } | |
464 | ||
465 | irqs = devm_kzalloc(&mc_dev->dev, irq_count * sizeof(irqs[0]), | |
466 | GFP_KERNEL); | |
467 | if (!irqs) | |
468 | return -ENOMEM; | |
469 | ||
470 | for (i = 0; i < irq_count; i++) { | |
471 | struct fsl_mc_resource *resource; | |
472 | ||
473 | error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_IRQ, | |
474 | &resource); | |
475 | if (error < 0) | |
476 | goto error_resource_alloc; | |
477 | ||
478 | irqs[i] = to_fsl_mc_irq(resource); | |
479 | res_allocated_count++; | |
480 | ||
481 | WARN_ON(irqs[i]->mc_dev); | |
482 | irqs[i]->mc_dev = mc_dev; | |
483 | irqs[i]->dev_irq_index = i; | |
484 | } | |
485 | ||
486 | mc_dev->irqs = irqs; | |
487 | return 0; | |
488 | ||
489 | error_resource_alloc: | |
490 | for (i = 0; i < res_allocated_count; i++) { | |
491 | irqs[i]->mc_dev = NULL; | |
492 | fsl_mc_resource_free(&irqs[i]->resource); | |
493 | } | |
494 | ||
495 | return error; | |
496 | } | |
497 | EXPORT_SYMBOL_GPL(fsl_mc_allocate_irqs); | |
498 | ||
499 | /* | |
500 | * It frees the IRQs that were allocated for a MC object device, by | |
501 | * returning them to the corresponding interrupt pool. | |
502 | */ | |
503 | void fsl_mc_free_irqs(struct fsl_mc_device *mc_dev) | |
504 | { | |
505 | int i; | |
506 | int irq_count; | |
507 | struct fsl_mc_bus *mc_bus; | |
508 | struct fsl_mc_device_irq **irqs = mc_dev->irqs; | |
509 | ||
510 | if (WARN_ON(!irqs)) | |
511 | return; | |
512 | ||
513 | irq_count = mc_dev->obj_desc.irq_count; | |
514 | ||
515 | if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) | |
516 | mc_bus = to_fsl_mc_bus(mc_dev); | |
517 | else | |
518 | mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent)); | |
519 | ||
520 | if (WARN_ON(!mc_bus->irq_resources)) | |
521 | return; | |
522 | ||
523 | for (i = 0; i < irq_count; i++) { | |
524 | WARN_ON(!irqs[i]->mc_dev); | |
525 | irqs[i]->mc_dev = NULL; | |
526 | fsl_mc_resource_free(&irqs[i]->resource); | |
527 | } | |
528 | ||
529 | mc_dev->irqs = NULL; | |
530 | } | |
531 | EXPORT_SYMBOL_GPL(fsl_mc_free_irqs); | |
532 | ||
36406955 SY |
533 | void fsl_mc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev) |
534 | { | |
535 | int pool_type; | |
536 | struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); | |
537 | ||
538 | for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++) { | |
539 | struct fsl_mc_resource_pool *res_pool = | |
540 | &mc_bus->resource_pools[pool_type]; | |
541 | ||
542 | res_pool->type = pool_type; | |
543 | res_pool->max_count = 0; | |
544 | res_pool->free_count = 0; | |
545 | res_pool->mc_bus = mc_bus; | |
546 | INIT_LIST_HEAD(&res_pool->free_list); | |
547 | mutex_init(&res_pool->mutex); | |
548 | } | |
549 | } | |
550 | ||
551 | static void fsl_mc_cleanup_resource_pool(struct fsl_mc_device *mc_bus_dev, | |
552 | enum fsl_mc_pool_type pool_type) | |
553 | { | |
554 | struct fsl_mc_resource *resource; | |
555 | struct fsl_mc_resource *next; | |
556 | struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); | |
557 | struct fsl_mc_resource_pool *res_pool = | |
558 | &mc_bus->resource_pools[pool_type]; | |
559 | int free_count = 0; | |
560 | ||
561 | WARN_ON(res_pool->type != pool_type); | |
562 | WARN_ON(res_pool->free_count != res_pool->max_count); | |
563 | ||
564 | list_for_each_entry_safe(resource, next, &res_pool->free_list, node) { | |
565 | free_count++; | |
566 | WARN_ON(resource->type != res_pool->type); | |
567 | WARN_ON(resource->parent_pool != res_pool); | |
568 | devm_kfree(&mc_bus_dev->dev, resource); | |
569 | } | |
570 | ||
571 | WARN_ON(free_count != res_pool->free_count); | |
572 | } | |
573 | ||
574 | void fsl_mc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev) | |
575 | { | |
576 | int pool_type; | |
577 | ||
578 | for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++) | |
579 | fsl_mc_cleanup_resource_pool(mc_bus_dev, pool_type); | |
580 | } | |
581 | ||
197f4d6a GR |
582 | /** |
583 | * fsl_mc_allocator_probe - callback invoked when an allocatable device is | |
584 | * being added to the system | |
585 | */ | |
586 | static int fsl_mc_allocator_probe(struct fsl_mc_device *mc_dev) | |
587 | { | |
588 | enum fsl_mc_pool_type pool_type; | |
589 | struct fsl_mc_device *mc_bus_dev; | |
590 | struct fsl_mc_bus *mc_bus; | |
09a38ab0 | 591 | int error; |
197f4d6a GR |
592 | |
593 | if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type))) | |
09a38ab0 | 594 | return -EINVAL; |
197f4d6a GR |
595 | |
596 | mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); | |
df5e9b5f | 597 | if (WARN_ON(!dev_is_fsl_mc(&mc_bus_dev->dev))) |
09a38ab0 | 598 | return -EINVAL; |
197f4d6a GR |
599 | |
600 | mc_bus = to_fsl_mc_bus(mc_bus_dev); | |
601 | error = object_type_to_pool_type(mc_dev->obj_desc.type, &pool_type); | |
602 | if (error < 0) | |
09a38ab0 | 603 | return error; |
197f4d6a GR |
604 | |
605 | error = fsl_mc_resource_pool_add_device(mc_bus, pool_type, mc_dev); | |
606 | if (error < 0) | |
09a38ab0 | 607 | return error; |
197f4d6a | 608 | |
6998d6ba GR |
609 | dev_dbg(&mc_dev->dev, |
610 | "Allocatable MC object device bound to fsl_mc_allocator driver"); | |
197f4d6a | 611 | return 0; |
197f4d6a GR |
612 | } |
613 | ||
614 | /** | |
615 | * fsl_mc_allocator_remove - callback invoked when an allocatable device is | |
616 | * being removed from the system | |
617 | */ | |
618 | static int fsl_mc_allocator_remove(struct fsl_mc_device *mc_dev) | |
619 | { | |
09a38ab0 | 620 | int error; |
197f4d6a GR |
621 | |
622 | if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type))) | |
09a38ab0 | 623 | return -EINVAL; |
197f4d6a | 624 | |
6958cd44 GR |
625 | if (mc_dev->resource) { |
626 | error = fsl_mc_resource_pool_remove_device(mc_dev); | |
627 | if (error < 0) | |
09a38ab0 | 628 | return error; |
6958cd44 | 629 | } |
197f4d6a | 630 | |
6998d6ba GR |
631 | dev_dbg(&mc_dev->dev, |
632 | "Allocatable MC object device unbound from fsl_mc_allocator driver"); | |
09a38ab0 | 633 | return 0; |
197f4d6a GR |
634 | } |
635 | ||
57538afb | 636 | static const struct fsl_mc_device_id match_id_table[] = { |
197f4d6a GR |
637 | { |
638 | .vendor = FSL_MC_VENDOR_FREESCALE, | |
639 | .obj_type = "dpbp", | |
197f4d6a GR |
640 | }, |
641 | { | |
642 | .vendor = FSL_MC_VENDOR_FREESCALE, | |
643 | .obj_type = "dpmcp", | |
197f4d6a GR |
644 | }, |
645 | { | |
646 | .vendor = FSL_MC_VENDOR_FREESCALE, | |
647 | .obj_type = "dpcon", | |
197f4d6a GR |
648 | }, |
649 | {.vendor = 0x0}, | |
650 | }; | |
651 | ||
652 | static struct fsl_mc_driver fsl_mc_allocator_driver = { | |
653 | .driver = { | |
654 | .name = "fsl_mc_allocator", | |
655 | .owner = THIS_MODULE, | |
656 | .pm = NULL, | |
657 | }, | |
658 | .match_id_table = match_id_table, | |
659 | .probe = fsl_mc_allocator_probe, | |
660 | .remove = fsl_mc_allocator_remove, | |
661 | }; | |
662 | ||
e91ffa9e GR |
663 | int __init fsl_mc_allocator_driver_init(void) |
664 | { | |
665 | return fsl_mc_driver_register(&fsl_mc_allocator_driver); | |
666 | } | |
197f4d6a | 667 | |
53360607 | 668 | void fsl_mc_allocator_driver_exit(void) |
e91ffa9e GR |
669 | { |
670 | fsl_mc_driver_unregister(&fsl_mc_allocator_driver); | |
671 | } |