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