Add the rt linux 4.1.3-rt3 as base
[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         uint32_t 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                                    mc_bus_dev->mc_handle,
266                                    &num_child_objects);
267         if (error < 0) {
268                 dev_err(&mc_bus_dev->dev, "dprc_get_obj_count() failed: %d\n",
269                         error);
270                 return error;
271         }
272
273         if (num_child_objects != 0) {
274                 int i;
275
276                 child_obj_desc_array =
277                     devm_kmalloc_array(&mc_bus_dev->dev, num_child_objects,
278                                        sizeof(*child_obj_desc_array),
279                                        GFP_KERNEL);
280                 if (!child_obj_desc_array)
281                         return -ENOMEM;
282
283                 /*
284                  * Discover objects currently present in the physical DPRC:
285                  */
286                 dprc_get_obj_failures = 0;
287                 for (i = 0; i < num_child_objects; i++) {
288                         struct dprc_obj_desc *obj_desc =
289                             &child_obj_desc_array[i];
290
291                         error = dprc_get_obj(mc_bus_dev->mc_io,
292                                              mc_bus_dev->mc_handle,
293                                              i, obj_desc);
294                         if (error < 0) {
295                                 dev_err(&mc_bus_dev->dev,
296                                         "dprc_get_obj(i=%d) failed: %d\n",
297                                         i, error);
298                                 /*
299                                  * Mark the obj entry as "invalid", by using the
300                                  * empty string as obj type:
301                                  */
302                                 obj_desc->type[0] = '\0';
303                                 obj_desc->id = error;
304                                 dprc_get_obj_failures++;
305                                 continue;
306                         }
307
308                         dev_dbg(&mc_bus_dev->dev,
309                                 "Discovered object: type %s, id %d\n",
310                                 obj_desc->type, obj_desc->id);
311                 }
312
313                 if (dprc_get_obj_failures != 0) {
314                         dev_err(&mc_bus_dev->dev,
315                                 "%d out of %d devices could not be retrieved\n",
316                                 dprc_get_obj_failures, num_child_objects);
317                 }
318         }
319
320         dprc_remove_devices(mc_bus_dev, child_obj_desc_array,
321                             num_child_objects);
322
323         dprc_add_new_devices(mc_bus_dev, child_obj_desc_array,
324                              num_child_objects);
325
326         if (child_obj_desc_array)
327                 devm_kfree(&mc_bus_dev->dev, child_obj_desc_array);
328
329         return 0;
330 }
331 EXPORT_SYMBOL_GPL(dprc_scan_objects);
332
333 /**
334  * dprc_scan_container - Scans a physical DPRC and synchronizes Linux bus state
335  *
336  * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
337  *
338  * Scans the physical DPRC and synchronizes the state of the Linux
339  * bus driver with the actual state of the MC by adding and removing
340  * devices as appropriate.
341  */
342 int dprc_scan_container(struct fsl_mc_device *mc_bus_dev)
343 {
344         int error;
345         struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
346
347         dprc_init_all_resource_pools(mc_bus_dev);
348
349         /*
350          * Discover objects in the DPRC:
351          */
352         mutex_lock(&mc_bus->scan_mutex);
353         error = dprc_scan_objects(mc_bus_dev);
354         mutex_unlock(&mc_bus->scan_mutex);
355         if (error < 0)
356                 goto error;
357
358         return 0;
359 error:
360         dprc_cleanup_all_resource_pools(mc_bus_dev);
361         return error;
362 }
363 EXPORT_SYMBOL_GPL(dprc_scan_container);
364
365 /**
366  * dprc_probe - callback invoked when a DPRC is being bound to this driver
367  *
368  * @mc_dev: Pointer to fsl-mc device representing a DPRC
369  *
370  * It opens the physical DPRC in the MC.
371  * It scans the DPRC to discover the MC objects contained in it.
372  * It creates the interrupt pool for the MC bus associated with the DPRC.
373  * It configures the interrupts for the DPRC device itself.
374  */
375 static int dprc_probe(struct fsl_mc_device *mc_dev)
376 {
377         int error;
378         size_t region_size;
379         struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
380
381         if (WARN_ON(strcmp(mc_dev->obj_desc.type, "dprc") != 0))
382                 return -EINVAL;
383
384         if (!mc_dev->mc_io) {
385                 /*
386                  * This is a child DPRC:
387                  */
388                 if (WARN_ON(mc_dev->obj_desc.region_count == 0))
389                         return -EINVAL;
390
391                 region_size = mc_dev->regions[0].end -
392                               mc_dev->regions[0].start + 1;
393
394                 error = fsl_create_mc_io(&mc_dev->dev,
395                                          mc_dev->regions[0].start,
396                                          region_size,
397                                          NULL, 0, &mc_dev->mc_io);
398                 if (error < 0)
399                         return error;
400         }
401
402         error = dprc_open(mc_dev->mc_io, mc_dev->obj_desc.id,
403                           &mc_dev->mc_handle);
404         if (error < 0) {
405                 dev_err(&mc_dev->dev, "dprc_open() failed: %d\n", error);
406                 goto error_cleanup_mc_io;
407         }
408
409         mutex_init(&mc_bus->scan_mutex);
410
411         /*
412          * Discover MC objects in DPRC object:
413          */
414         error = dprc_scan_container(mc_dev);
415         if (error < 0)
416                 goto error_cleanup_open;
417
418         dev_info(&mc_dev->dev, "DPRC device bound to driver");
419         return 0;
420
421 error_cleanup_open:
422         (void)dprc_close(mc_dev->mc_io, mc_dev->mc_handle);
423
424 error_cleanup_mc_io:
425         fsl_destroy_mc_io(mc_dev->mc_io);
426         return error;
427 }
428
429 /**
430  * dprc_remove - callback invoked when a DPRC is being unbound from this driver
431  *
432  * @mc_dev: Pointer to fsl-mc device representing the DPRC
433  *
434  * It removes the DPRC's child objects from Linux (not from the MC) and
435  * closes the DPRC device in the MC.
436  * It tears down the interrupts that were configured for the DPRC device.
437  * It destroys the interrupt pool associated with this MC bus.
438  */
439 static int dprc_remove(struct fsl_mc_device *mc_dev)
440 {
441         int error;
442
443         if (WARN_ON(strcmp(mc_dev->obj_desc.type, "dprc") != 0))
444                 return -EINVAL;
445         if (WARN_ON(!mc_dev->mc_io))
446                 return -EINVAL;
447
448         device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove);
449         dprc_cleanup_all_resource_pools(mc_dev);
450         error = dprc_close(mc_dev->mc_io, mc_dev->mc_handle);
451         if (error < 0)
452                 dev_err(&mc_dev->dev, "dprc_close() failed: %d\n", error);
453
454         dev_info(&mc_dev->dev, "DPRC device unbound from driver");
455         return 0;
456 }
457
458 static const struct fsl_mc_device_match_id match_id_table[] = {
459         {
460          .vendor = FSL_MC_VENDOR_FREESCALE,
461          .obj_type = "dprc",
462          .ver_major = DPRC_VER_MAJOR,
463          .ver_minor = DPRC_VER_MINOR},
464         {.vendor = 0x0},
465 };
466
467 static struct fsl_mc_driver dprc_driver = {
468         .driver = {
469                    .name = FSL_MC_DPRC_DRIVER_NAME,
470                    .owner = THIS_MODULE,
471                    .pm = NULL,
472                    },
473         .match_id_table = match_id_table,
474         .probe = dprc_probe,
475         .remove = dprc_remove,
476 };
477
478 int __init dprc_driver_init(void)
479 {
480         return fsl_mc_driver_register(&dprc_driver);
481 }
482
483 void __exit dprc_driver_exit(void)
484 {
485         fsl_mc_driver_unregister(&dprc_driver);
486 }