These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / staging / fsl-mc / bus / dprc-driver.c
1 /*
2  * Freescale data path resource container (DPRC) driver
3  *
4  * Copyright (C) 2014 Freescale Semiconductor, Inc.
5  * Author: German Rivera <German.Rivera@freescale.com>
6  *
7  * This file is licensed under the terms of the GNU General Public
8  * License version 2. This program is licensed "as is" without any
9  * warranty of any kind, whether express or implied.
10  */
11
12 #include "../include/mc-private.h"
13 #include "../include/mc-sys.h"
14 #include <linux/module.h>
15 #include <linux/slab.h>
16 #include "dprc-cmd.h"
17
18 struct dprc_child_objs {
19         int child_count;
20         struct dprc_obj_desc *child_array;
21 };
22
23 static int __fsl_mc_device_remove_if_not_in_mc(struct device *dev, void *data)
24 {
25         int i;
26         struct dprc_child_objs *objs;
27         struct fsl_mc_device *mc_dev;
28
29         WARN_ON(!dev);
30         WARN_ON(!data);
31         mc_dev = to_fsl_mc_device(dev);
32         objs = data;
33
34         for (i = 0; i < objs->child_count; i++) {
35                 struct dprc_obj_desc *obj_desc = &objs->child_array[i];
36
37                 if (strlen(obj_desc->type) != 0 &&
38                     FSL_MC_DEVICE_MATCH(mc_dev, obj_desc))
39                         break;
40         }
41
42         if (i == objs->child_count)
43                 fsl_mc_device_remove(mc_dev);
44
45         return 0;
46 }
47
48 static int __fsl_mc_device_remove(struct device *dev, void *data)
49 {
50         WARN_ON(!dev);
51         WARN_ON(data);
52         fsl_mc_device_remove(to_fsl_mc_device(dev));
53         return 0;
54 }
55
56 /**
57  * dprc_remove_devices - Removes devices for objects removed from a DPRC
58  *
59  * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
60  * @obj_desc_array: array of object descriptors for child objects currently
61  * present in the DPRC in the MC.
62  * @num_child_objects_in_mc: number of entries in obj_desc_array
63  *
64  * Synchronizes the state of the Linux bus driver with the actual state of
65  * the MC by removing devices that represent MC objects that have
66  * been dynamically removed in the physical DPRC.
67  */
68 static void dprc_remove_devices(struct fsl_mc_device *mc_bus_dev,
69                                 struct dprc_obj_desc *obj_desc_array,
70                                 int num_child_objects_in_mc)
71 {
72         if (num_child_objects_in_mc != 0) {
73                 /*
74                  * Remove child objects that are in the DPRC in Linux,
75                  * but not in the MC:
76                  */
77                 struct dprc_child_objs objs;
78
79                 objs.child_count = num_child_objects_in_mc;
80                 objs.child_array = obj_desc_array;
81                 device_for_each_child(&mc_bus_dev->dev, &objs,
82                                       __fsl_mc_device_remove_if_not_in_mc);
83         } else {
84                 /*
85                  * There are no child objects for this DPRC in the MC.
86                  * So, remove all the child devices from Linux:
87                  */
88                 device_for_each_child(&mc_bus_dev->dev, NULL,
89                                       __fsl_mc_device_remove);
90         }
91 }
92
93 static int __fsl_mc_device_match(struct device *dev, void *data)
94 {
95         struct dprc_obj_desc *obj_desc = data;
96         struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
97
98         return FSL_MC_DEVICE_MATCH(mc_dev, obj_desc);
99 }
100
101 static struct fsl_mc_device *fsl_mc_device_lookup(struct dprc_obj_desc
102                                                                 *obj_desc,
103                                                   struct fsl_mc_device
104                                                                 *mc_bus_dev)
105 {
106         struct device *dev;
107
108         dev = device_find_child(&mc_bus_dev->dev, obj_desc,
109                                 __fsl_mc_device_match);
110
111         return dev ? to_fsl_mc_device(dev) : NULL;
112 }
113
114 /**
115  * check_plugged_state_change - Check change in an MC object's plugged state
116  *
117  * @mc_dev: pointer to the fsl-mc device for a given MC object
118  * @obj_desc: pointer to the MC object's descriptor in the MC
119  *
120  * If the plugged state has changed from unplugged to plugged, the fsl-mc
121  * device is bound to the corresponding device driver.
122  * If the plugged state has changed from plugged to unplugged, the fsl-mc
123  * device is unbound from the corresponding device driver.
124  */
125 static void check_plugged_state_change(struct fsl_mc_device *mc_dev,
126                                        struct dprc_obj_desc *obj_desc)
127 {
128         int error;
129         u32 plugged_flag_at_mc =
130                         (obj_desc->state & DPRC_OBJ_STATE_PLUGGED);
131
132         if (plugged_flag_at_mc !=
133             (mc_dev->obj_desc.state & DPRC_OBJ_STATE_PLUGGED)) {
134                 if (plugged_flag_at_mc) {
135                         mc_dev->obj_desc.state |= DPRC_OBJ_STATE_PLUGGED;
136                         error = device_attach(&mc_dev->dev);
137                         if (error < 0) {
138                                 dev_err(&mc_dev->dev,
139                                         "device_attach() failed: %d\n",
140                                         error);
141                         }
142                 } else {
143                         mc_dev->obj_desc.state &= ~DPRC_OBJ_STATE_PLUGGED;
144                         device_release_driver(&mc_dev->dev);
145                 }
146         }
147 }
148
149 /**
150  * dprc_add_new_devices - Adds devices to the logical bus for a DPRC
151  *
152  * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
153  * @obj_desc_array: array of device descriptors for child devices currently
154  * present in the physical DPRC.
155  * @num_child_objects_in_mc: number of entries in obj_desc_array
156  *
157  * Synchronizes the state of the Linux bus driver with the actual
158  * state of the MC by adding objects that have been newly discovered
159  * in the physical DPRC.
160  */
161 static void dprc_add_new_devices(struct fsl_mc_device *mc_bus_dev,
162                                  struct dprc_obj_desc *obj_desc_array,
163                                  int num_child_objects_in_mc)
164 {
165         int error;
166         int i;
167
168         for (i = 0; i < num_child_objects_in_mc; i++) {
169                 struct fsl_mc_device *child_dev;
170                 struct dprc_obj_desc *obj_desc = &obj_desc_array[i];
171
172                 if (strlen(obj_desc->type) == 0)
173                         continue;
174
175                 /*
176                  * Check if device is already known to Linux:
177                  */
178                 child_dev = fsl_mc_device_lookup(obj_desc, mc_bus_dev);
179                 if (child_dev) {
180                         check_plugged_state_change(child_dev, obj_desc);
181                         continue;
182                 }
183
184                 error = fsl_mc_device_add(obj_desc, NULL, &mc_bus_dev->dev,
185                                           &child_dev);
186                 if (error < 0)
187                         continue;
188         }
189 }
190
191 static void dprc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev)
192 {
193         int pool_type;
194         struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
195
196         for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++) {
197                 struct fsl_mc_resource_pool *res_pool =
198                     &mc_bus->resource_pools[pool_type];
199
200                 res_pool->type = pool_type;
201                 res_pool->max_count = 0;
202                 res_pool->free_count = 0;
203                 res_pool->mc_bus = mc_bus;
204                 INIT_LIST_HEAD(&res_pool->free_list);
205                 mutex_init(&res_pool->mutex);
206         }
207 }
208
209 static void dprc_cleanup_resource_pool(struct fsl_mc_device *mc_bus_dev,
210                                        enum fsl_mc_pool_type pool_type)
211 {
212         struct fsl_mc_resource *resource;
213         struct fsl_mc_resource *next;
214         struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
215         struct fsl_mc_resource_pool *res_pool =
216                                         &mc_bus->resource_pools[pool_type];
217         int free_count = 0;
218
219         WARN_ON(res_pool->type != pool_type);
220         WARN_ON(res_pool->free_count != res_pool->max_count);
221
222         list_for_each_entry_safe(resource, next, &res_pool->free_list, node) {
223                 free_count++;
224                 WARN_ON(resource->type != res_pool->type);
225                 WARN_ON(resource->parent_pool != res_pool);
226                 devm_kfree(&mc_bus_dev->dev, resource);
227         }
228
229         WARN_ON(free_count != res_pool->free_count);
230 }
231
232 static void dprc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev)
233 {
234         int pool_type;
235
236         for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++)
237                 dprc_cleanup_resource_pool(mc_bus_dev, pool_type);
238 }
239
240 /**
241  * dprc_scan_objects - Discover objects in a DPRC
242  *
243  * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
244  *
245  * Detects objects added and removed from a DPRC and synchronizes the
246  * state of the Linux bus driver, MC by adding and removing
247  * devices accordingly.
248  * Two types of devices can be found in a DPRC: allocatable objects (e.g.,
249  * dpbp, dpmcp) and non-allocatable devices (e.g., dprc, dpni).
250  * All allocatable devices needed to be probed before all non-allocatable
251  * devices, to ensure that device drivers for non-allocatable
252  * devices can allocate any type of allocatable devices.
253  * That is, we need to ensure that the corresponding resource pools are
254  * populated before they can get allocation requests from probe callbacks
255  * of the device drivers for the non-allocatable devices.
256  */
257 int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev)
258 {
259         int num_child_objects;
260         int dprc_get_obj_failures;
261         int error;
262         struct dprc_obj_desc *child_obj_desc_array = NULL;
263
264         error = dprc_get_obj_count(mc_bus_dev->mc_io,
265                                    0,
266                                    mc_bus_dev->mc_handle,
267                                    &num_child_objects);
268         if (error < 0) {
269                 dev_err(&mc_bus_dev->dev, "dprc_get_obj_count() failed: %d\n",
270                         error);
271                 return error;
272         }
273
274         if (num_child_objects != 0) {
275                 int i;
276
277                 child_obj_desc_array =
278                     devm_kmalloc_array(&mc_bus_dev->dev, num_child_objects,
279                                        sizeof(*child_obj_desc_array),
280                                        GFP_KERNEL);
281                 if (!child_obj_desc_array)
282                         return -ENOMEM;
283
284                 /*
285                  * Discover objects currently present in the physical DPRC:
286                  */
287                 dprc_get_obj_failures = 0;
288                 for (i = 0; i < num_child_objects; i++) {
289                         struct dprc_obj_desc *obj_desc =
290                             &child_obj_desc_array[i];
291
292                         error = dprc_get_obj(mc_bus_dev->mc_io,
293                                              0,
294                                              mc_bus_dev->mc_handle,
295                                              i, obj_desc);
296                         if (error < 0) {
297                                 dev_err(&mc_bus_dev->dev,
298                                         "dprc_get_obj(i=%d) failed: %d\n",
299                                         i, error);
300                                 /*
301                                  * Mark the obj entry as "invalid", by using the
302                                  * empty string as obj type:
303                                  */
304                                 obj_desc->type[0] = '\0';
305                                 obj_desc->id = error;
306                                 dprc_get_obj_failures++;
307                                 continue;
308                         }
309
310                         dev_dbg(&mc_bus_dev->dev,
311                                 "Discovered object: type %s, id %d\n",
312                                 obj_desc->type, obj_desc->id);
313                 }
314
315                 if (dprc_get_obj_failures != 0) {
316                         dev_err(&mc_bus_dev->dev,
317                                 "%d out of %d devices could not be retrieved\n",
318                                 dprc_get_obj_failures, num_child_objects);
319                 }
320         }
321
322         dprc_remove_devices(mc_bus_dev, child_obj_desc_array,
323                             num_child_objects);
324
325         dprc_add_new_devices(mc_bus_dev, child_obj_desc_array,
326                              num_child_objects);
327
328         if (child_obj_desc_array)
329                 devm_kfree(&mc_bus_dev->dev, child_obj_desc_array);
330
331         return 0;
332 }
333 EXPORT_SYMBOL_GPL(dprc_scan_objects);
334
335 /**
336  * dprc_scan_container - Scans a physical DPRC and synchronizes Linux bus state
337  *
338  * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
339  *
340  * Scans the physical DPRC and synchronizes the state of the Linux
341  * bus driver with the actual state of the MC by adding and removing
342  * devices as appropriate.
343  */
344 int dprc_scan_container(struct fsl_mc_device *mc_bus_dev)
345 {
346         int error;
347         struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
348
349         dprc_init_all_resource_pools(mc_bus_dev);
350
351         /*
352          * Discover objects in the DPRC:
353          */
354         mutex_lock(&mc_bus->scan_mutex);
355         error = dprc_scan_objects(mc_bus_dev);
356         mutex_unlock(&mc_bus->scan_mutex);
357         if (error < 0)
358                 goto error;
359
360         return 0;
361 error:
362         dprc_cleanup_all_resource_pools(mc_bus_dev);
363         return error;
364 }
365 EXPORT_SYMBOL_GPL(dprc_scan_container);
366
367 /**
368  * dprc_probe - callback invoked when a DPRC is being bound to this driver
369  *
370  * @mc_dev: Pointer to fsl-mc device representing a DPRC
371  *
372  * It opens the physical DPRC in the MC.
373  * It scans the DPRC to discover the MC objects contained in it.
374  * It creates the interrupt pool for the MC bus associated with the DPRC.
375  * It configures the interrupts for the DPRC device itself.
376  */
377 static int dprc_probe(struct fsl_mc_device *mc_dev)
378 {
379         int error;
380         size_t region_size;
381         struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
382
383         if (WARN_ON(strcmp(mc_dev->obj_desc.type, "dprc") != 0))
384                 return -EINVAL;
385
386         if (!mc_dev->mc_io) {
387                 /*
388                  * This is a child DPRC:
389                  */
390                 if (WARN_ON(mc_dev->obj_desc.region_count == 0))
391                         return -EINVAL;
392
393                 region_size = mc_dev->regions[0].end -
394                               mc_dev->regions[0].start + 1;
395
396                 error = fsl_create_mc_io(&mc_dev->dev,
397                                          mc_dev->regions[0].start,
398                                          region_size,
399                                          NULL, 0, &mc_dev->mc_io);
400                 if (error < 0)
401                         return error;
402         }
403
404         error = dprc_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id,
405                           &mc_dev->mc_handle);
406         if (error < 0) {
407                 dev_err(&mc_dev->dev, "dprc_open() failed: %d\n", error);
408                 goto error_cleanup_mc_io;
409         }
410
411         mutex_init(&mc_bus->scan_mutex);
412
413         /*
414          * Discover MC objects in DPRC object:
415          */
416         error = dprc_scan_container(mc_dev);
417         if (error < 0)
418                 goto error_cleanup_open;
419
420         dev_info(&mc_dev->dev, "DPRC device bound to driver");
421         return 0;
422
423 error_cleanup_open:
424         (void)dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle);
425
426 error_cleanup_mc_io:
427         fsl_destroy_mc_io(mc_dev->mc_io);
428         return error;
429 }
430
431 /**
432  * dprc_remove - callback invoked when a DPRC is being unbound from this driver
433  *
434  * @mc_dev: Pointer to fsl-mc device representing the DPRC
435  *
436  * It removes the DPRC's child objects from Linux (not from the MC) and
437  * closes the DPRC device in the MC.
438  * It tears down the interrupts that were configured for the DPRC device.
439  * It destroys the interrupt pool associated with this MC bus.
440  */
441 static int dprc_remove(struct fsl_mc_device *mc_dev)
442 {
443         int error;
444
445         if (WARN_ON(strcmp(mc_dev->obj_desc.type, "dprc") != 0))
446                 return -EINVAL;
447         if (WARN_ON(!mc_dev->mc_io))
448                 return -EINVAL;
449
450         device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove);
451         dprc_cleanup_all_resource_pools(mc_dev);
452         error = dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle);
453         if (error < 0)
454                 dev_err(&mc_dev->dev, "dprc_close() failed: %d\n", error);
455
456         dev_info(&mc_dev->dev, "DPRC device unbound from driver");
457         return 0;
458 }
459
460 static const struct fsl_mc_device_match_id match_id_table[] = {
461         {
462          .vendor = FSL_MC_VENDOR_FREESCALE,
463          .obj_type = "dprc",
464          .ver_major = DPRC_VER_MAJOR,
465          .ver_minor = DPRC_VER_MINOR},
466         {.vendor = 0x0},
467 };
468
469 static struct fsl_mc_driver dprc_driver = {
470         .driver = {
471                    .name = FSL_MC_DPRC_DRIVER_NAME,
472                    .owner = THIS_MODULE,
473                    .pm = NULL,
474                    },
475         .match_id_table = match_id_table,
476         .probe = dprc_probe,
477         .remove = dprc_remove,
478 };
479
480 int __init dprc_driver_init(void)
481 {
482         return fsl_mc_driver_register(&dprc_driver);
483 }
484
485 void dprc_driver_exit(void)
486 {
487         fsl_mc_driver_unregister(&dprc_driver);
488 }