Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc
[linux-2.6-block.git] / drivers / staging / fsl-mc / bus / mc-bus.c
CommitLineData
bbf9d17d
GR
1/*
2 * Freescale Management Complex (MC) bus 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 <linux/module.h>
14#include <linux/of_device.h>
15#include <linux/of_address.h>
16#include <linux/ioport.h>
17#include <linux/slab.h>
18#include <linux/limits.h>
660a24bf
GR
19#include <linux/bitops.h>
20#include <linux/msi.h>
bbf9d17d
GR
21#include "../include/dpmng.h"
22#include "../include/mc-sys.h"
23#include "dprc-cmd.h"
24
25static struct kmem_cache *mc_dev_cache;
26
b55f00c6
IK
27static bool fsl_mc_is_root_dprc(struct device *dev);
28
bbf9d17d
GR
29/**
30 * fsl_mc_bus_match - device to driver matching callback
31 * @dev: the MC object device structure to match against
32 * @drv: the device driver to search for matching MC object device id
33 * structures
34 *
35 * Returns 1 on success, 0 otherwise.
36 */
37static int fsl_mc_bus_match(struct device *dev, struct device_driver *drv)
38{
39 const struct fsl_mc_device_match_id *id;
40 struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
41 struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(drv);
42 bool found = false;
3b0a9b10
GR
43 bool major_version_mismatch = false;
44 bool minor_version_mismatch = false;
bbf9d17d 45
14f92805 46 if (WARN_ON(!fsl_mc_bus_exists()))
bbf9d17d
GR
47 goto out;
48
49 if (!mc_drv->match_id_table)
50 goto out;
51
52 /*
53 * If the object is not 'plugged' don't match.
54 * Only exception is the root DPRC, which is a special case.
55 */
56 if ((mc_dev->obj_desc.state & DPRC_OBJ_STATE_PLUGGED) == 0 &&
b55f00c6 57 !fsl_mc_is_root_dprc(&mc_dev->dev))
bbf9d17d
GR
58 goto out;
59
60 /*
61 * Traverse the match_id table of the given driver, trying to find
62 * a matching for the given MC object device.
63 */
64 for (id = mc_drv->match_id_table; id->vendor != 0x0; id++) {
65 if (id->vendor == mc_dev->obj_desc.vendor &&
3b0a9b10
GR
66 strcmp(id->obj_type, mc_dev->obj_desc.type) == 0) {
67 if (id->ver_major == mc_dev->obj_desc.ver_major) {
68 found = true;
69 if (id->ver_minor != mc_dev->obj_desc.ver_minor)
70 minor_version_mismatch = true;
71 } else {
72 major_version_mismatch = true;
73 }
74
bbf9d17d
GR
75 break;
76 }
77 }
78
3b0a9b10
GR
79 if (major_version_mismatch) {
80 dev_warn(dev,
81 "Major version mismatch: driver version %u.%u, MC object version %u.%u\n",
82 id->ver_major, id->ver_minor,
83 mc_dev->obj_desc.ver_major,
84 mc_dev->obj_desc.ver_minor);
85 } else if (minor_version_mismatch) {
86 dev_warn(dev,
87 "Minor version mismatch: driver version %u.%u, MC object version %u.%u\n",
88 id->ver_major, id->ver_minor,
89 mc_dev->obj_desc.ver_major,
90 mc_dev->obj_desc.ver_minor);
91 }
92
bbf9d17d
GR
93out:
94 dev_dbg(dev, "%smatched\n", found ? "" : "not ");
95 return found;
96}
97
98/**
99 * fsl_mc_bus_uevent - callback invoked when a device is added
100 */
101static int fsl_mc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
102{
103 pr_debug("%s invoked\n", __func__);
104 return 0;
105}
106
107struct bus_type fsl_mc_bus_type = {
108 .name = "fsl-mc",
109 .match = fsl_mc_bus_match,
110 .uevent = fsl_mc_bus_uevent,
111};
112EXPORT_SYMBOL_GPL(fsl_mc_bus_type);
113
565b9450
IK
114static atomic_t root_dprc_count = ATOMIC_INIT(0);
115
bbf9d17d
GR
116static int fsl_mc_driver_probe(struct device *dev)
117{
118 struct fsl_mc_driver *mc_drv;
119 struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
120 int error;
121
122 if (WARN_ON(!dev->driver))
123 return -EINVAL;
124
125 mc_drv = to_fsl_mc_driver(dev->driver);
126 if (WARN_ON(!mc_drv->probe))
127 return -EINVAL;
128
129 error = mc_drv->probe(mc_dev);
130 if (error < 0) {
131 dev_err(dev, "MC object device probe callback failed: %d\n",
132 error);
133 return error;
134 }
135
136 return 0;
137}
138
139static int fsl_mc_driver_remove(struct device *dev)
140{
141 struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
142 struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
143 int error;
144
145 if (WARN_ON(!dev->driver))
146 return -EINVAL;
147
148 error = mc_drv->remove(mc_dev);
149 if (error < 0) {
150 dev_err(dev,
151 "MC object device remove callback failed: %d\n",
152 error);
153 return error;
154 }
155
156 return 0;
157}
158
159static void fsl_mc_driver_shutdown(struct device *dev)
160{
161 struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
162 struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
163
164 mc_drv->shutdown(mc_dev);
165}
166
167/**
168 * __fsl_mc_driver_register - registers a child device driver with the
169 * MC bus
170 *
171 * This function is implicitly invoked from the registration function of
172 * fsl_mc device drivers, which is generated by the
173 * module_fsl_mc_driver() macro.
174 */
175int __fsl_mc_driver_register(struct fsl_mc_driver *mc_driver,
176 struct module *owner)
177{
178 int error;
179
180 mc_driver->driver.owner = owner;
181 mc_driver->driver.bus = &fsl_mc_bus_type;
182
183 if (mc_driver->probe)
184 mc_driver->driver.probe = fsl_mc_driver_probe;
185
186 if (mc_driver->remove)
187 mc_driver->driver.remove = fsl_mc_driver_remove;
188
189 if (mc_driver->shutdown)
190 mc_driver->driver.shutdown = fsl_mc_driver_shutdown;
191
192 error = driver_register(&mc_driver->driver);
193 if (error < 0) {
194 pr_err("driver_register() failed for %s: %d\n",
195 mc_driver->driver.name, error);
196 return error;
197 }
198
199 pr_info("MC object device driver %s registered\n",
200 mc_driver->driver.name);
201 return 0;
202}
203EXPORT_SYMBOL_GPL(__fsl_mc_driver_register);
204
205/**
206 * fsl_mc_driver_unregister - unregisters a device driver from the
207 * MC bus
208 */
209void fsl_mc_driver_unregister(struct fsl_mc_driver *mc_driver)
210{
211 driver_unregister(&mc_driver->driver);
212}
213EXPORT_SYMBOL_GPL(fsl_mc_driver_unregister);
214
14f92805
IK
215/**
216 * fsl_mc_bus_exists - check if a root dprc exists
217 */
218bool fsl_mc_bus_exists(void)
219{
565b9450 220 return atomic_read(&root_dprc_count) > 0;
14f92805
IK
221}
222EXPORT_SYMBOL_GPL(fsl_mc_bus_exists);
223
e4ea40f9
IK
224/**
225* fsl_mc_get_root_dprc - function to traverse to the root dprc
226*/
227static void fsl_mc_get_root_dprc(struct device *dev,
228 struct device **root_dprc_dev)
229{
230 if (WARN_ON(!dev)) {
231 *root_dprc_dev = NULL;
232 } else if (WARN_ON(dev->bus != &fsl_mc_bus_type)) {
233 *root_dprc_dev = NULL;
234 } else {
235 *root_dprc_dev = dev;
236 while ((*root_dprc_dev)->parent->bus == &fsl_mc_bus_type)
237 *root_dprc_dev = (*root_dprc_dev)->parent;
238 }
239}
240
b55f00c6
IK
241/**
242 * fsl_mc_is_root_dprc - function to check if a given device is a root dprc
243 */
244static bool fsl_mc_is_root_dprc(struct device *dev)
245{
e4ea40f9
IK
246 struct device *root_dprc_dev;
247
248 fsl_mc_get_root_dprc(dev, &root_dprc_dev);
249 if (!root_dprc_dev)
250 return false;
9397ce21 251 return dev == root_dprc_dev;
b55f00c6
IK
252}
253
bbf9d17d 254static int get_dprc_icid(struct fsl_mc_io *mc_io,
ba72f25b 255 int container_id, u16 *icid)
bbf9d17d 256{
a2f9ff6c 257 u16 dprc_handle;
bbf9d17d
GR
258 struct dprc_attributes attr;
259 int error;
260
1ee695fa 261 error = dprc_open(mc_io, 0, container_id, &dprc_handle);
bbf9d17d 262 if (error < 0) {
2e115901 263 dev_err(mc_io->dev, "dprc_open() failed: %d\n", error);
bbf9d17d
GR
264 return error;
265 }
266
267 memset(&attr, 0, sizeof(attr));
1ee695fa 268 error = dprc_get_attributes(mc_io, 0, dprc_handle, &attr);
bbf9d17d 269 if (error < 0) {
2e115901 270 dev_err(mc_io->dev, "dprc_get_attributes() failed: %d\n",
454b0ec8 271 error);
bbf9d17d
GR
272 goto common_cleanup;
273 }
274
275 *icid = attr.icid;
276 error = 0;
277
278common_cleanup:
1ee695fa 279 (void)dprc_close(mc_io, 0, dprc_handle);
bbf9d17d
GR
280 return error;
281}
282
e4ea40f9
IK
283static int translate_mc_addr(struct fsl_mc_device *mc_dev,
284 enum dprc_region_type mc_region_type,
1ee695fa 285 u64 mc_offset, phys_addr_t *phys_addr)
bbf9d17d
GR
286{
287 int i;
e4ea40f9
IK
288 struct device *root_dprc_dev;
289 struct fsl_mc *mc;
290
291 fsl_mc_get_root_dprc(&mc_dev->dev, &root_dprc_dev);
292 if (WARN_ON(!root_dprc_dev))
293 return -EINVAL;
294 mc = dev_get_drvdata(root_dprc_dev->parent);
bbf9d17d
GR
295
296 if (mc->num_translation_ranges == 0) {
297 /*
298 * Do identity mapping:
299 */
1ee695fa 300 *phys_addr = mc_offset;
bbf9d17d
GR
301 return 0;
302 }
303
304 for (i = 0; i < mc->num_translation_ranges; i++) {
305 struct fsl_mc_addr_translation_range *range =
306 &mc->translation_ranges[i];
307
1ee695fa
GR
308 if (mc_region_type == range->mc_region_type &&
309 mc_offset >= range->start_mc_offset &&
310 mc_offset < range->end_mc_offset) {
bbf9d17d 311 *phys_addr = range->start_phys_addr +
1ee695fa 312 (mc_offset - range->start_mc_offset);
bbf9d17d
GR
313 return 0;
314 }
315 }
316
317 return -EFAULT;
318}
319
320static int fsl_mc_device_get_mmio_regions(struct fsl_mc_device *mc_dev,
321 struct fsl_mc_device *mc_bus_dev)
322{
323 int i;
324 int error;
325 struct resource *regions;
326 struct dprc_obj_desc *obj_desc = &mc_dev->obj_desc;
327 struct device *parent_dev = mc_dev->dev.parent;
1ee695fa
GR
328 enum dprc_region_type mc_region_type;
329
330 if (strcmp(obj_desc->type, "dprc") == 0 ||
331 strcmp(obj_desc->type, "dpmcp") == 0) {
332 mc_region_type = DPRC_REGION_TYPE_MC_PORTAL;
333 } else if (strcmp(obj_desc->type, "dpio") == 0) {
334 mc_region_type = DPRC_REGION_TYPE_QBMAN_PORTAL;
335 } else {
336 /*
337 * This function should not have been called for this MC object
338 * type, as this object type is not supposed to have MMIO
339 * regions
340 */
341 WARN_ON(true);
342 return -EINVAL;
343 }
bbf9d17d
GR
344
345 regions = kmalloc_array(obj_desc->region_count,
346 sizeof(regions[0]), GFP_KERNEL);
347 if (!regions)
348 return -ENOMEM;
349
350 for (i = 0; i < obj_desc->region_count; i++) {
351 struct dprc_region_desc region_desc;
352
353 error = dprc_get_obj_region(mc_bus_dev->mc_io,
1ee695fa 354 0,
bbf9d17d
GR
355 mc_bus_dev->mc_handle,
356 obj_desc->type,
357 obj_desc->id, i, &region_desc);
358 if (error < 0) {
359 dev_err(parent_dev,
360 "dprc_get_obj_region() failed: %d\n", error);
361 goto error_cleanup_regions;
362 }
363
bbf9d17d 364 WARN_ON(region_desc.size == 0);
e4ea40f9 365 error = translate_mc_addr(mc_dev, mc_region_type,
1ee695fa 366 region_desc.base_offset,
bbf9d17d
GR
367 &regions[i].start);
368 if (error < 0) {
369 dev_err(parent_dev,
1ee695fa
GR
370 "Invalid MC offset: %#x (for %s.%d\'s region %d)\n",
371 region_desc.base_offset,
372 obj_desc->type, obj_desc->id, i);
bbf9d17d
GR
373 goto error_cleanup_regions;
374 }
375
376 regions[i].end = regions[i].start + region_desc.size - 1;
377 regions[i].name = "fsl-mc object MMIO region";
378 regions[i].flags = IORESOURCE_IO;
379 }
380
381 mc_dev->regions = regions;
382 return 0;
383
384error_cleanup_regions:
385 kfree(regions);
386 return error;
387}
388
389/**
390 * Add a newly discovered MC object device to be visible in Linux
391 */
392int fsl_mc_device_add(struct dprc_obj_desc *obj_desc,
393 struct fsl_mc_io *mc_io,
394 struct device *parent_dev,
395 struct fsl_mc_device **new_mc_dev)
396{
397 int error;
398 struct fsl_mc_device *mc_dev = NULL;
399 struct fsl_mc_bus *mc_bus = NULL;
400 struct fsl_mc_device *parent_mc_dev;
401
402 if (parent_dev->bus == &fsl_mc_bus_type)
403 parent_mc_dev = to_fsl_mc_device(parent_dev);
404 else
405 parent_mc_dev = NULL;
406
407 if (strcmp(obj_desc->type, "dprc") == 0) {
408 /*
409 * Allocate an MC bus device object:
410 */
411 mc_bus = devm_kzalloc(parent_dev, sizeof(*mc_bus), GFP_KERNEL);
412 if (!mc_bus)
413 return -ENOMEM;
414
415 mc_dev = &mc_bus->mc_dev;
416 } else {
417 /*
418 * Allocate a regular fsl_mc_device object:
419 */
420 mc_dev = kmem_cache_zalloc(mc_dev_cache, GFP_KERNEL);
421 if (!mc_dev)
422 return -ENOMEM;
423 }
424
425 mc_dev->obj_desc = *obj_desc;
426 mc_dev->mc_io = mc_io;
427 device_initialize(&mc_dev->dev);
428 mc_dev->dev.parent = parent_dev;
429 mc_dev->dev.bus = &fsl_mc_bus_type;
77371fbd 430 dev_set_name(&mc_dev->dev, "%s.%d", obj_desc->type, obj_desc->id);
bbf9d17d
GR
431
432 if (strcmp(obj_desc->type, "dprc") == 0) {
433 struct fsl_mc_io *mc_io2;
434
435 mc_dev->flags |= FSL_MC_IS_DPRC;
436
437 /*
438 * To get the DPRC's ICID, we need to open the DPRC
439 * in get_dprc_icid(). For child DPRCs, we do so using the
440 * parent DPRC's MC portal instead of the child DPRC's MC
441 * portal, in case the child DPRC is already opened with
442 * its own portal (e.g., the DPRC used by AIOP).
443 *
444 * NOTE: There cannot be more than one active open for a
445 * given MC object, using the same MC portal.
446 */
447 if (parent_mc_dev) {
448 /*
449 * device being added is a child DPRC device
450 */
451 mc_io2 = parent_mc_dev->mc_io;
452 } else {
453 /*
454 * device being added is the root DPRC device
455 */
456 if (WARN_ON(!mc_io)) {
457 error = -EINVAL;
458 goto error_cleanup_dev;
459 }
460
461 mc_io2 = mc_io;
462
565b9450 463 atomic_inc(&root_dprc_count);
bbf9d17d
GR
464 }
465
466 error = get_dprc_icid(mc_io2, obj_desc->id, &mc_dev->icid);
467 if (error < 0)
468 goto error_cleanup_dev;
469 } else {
470 /*
471 * A non-DPRC MC object device has to be a child of another
472 * MC object (specifically a DPRC object)
473 */
474 mc_dev->icid = parent_mc_dev->icid;
475 mc_dev->dma_mask = FSL_MC_DEFAULT_DMA_MASK;
476 mc_dev->dev.dma_mask = &mc_dev->dma_mask;
660a24bf
GR
477 dev_set_msi_domain(&mc_dev->dev,
478 dev_get_msi_domain(&parent_mc_dev->dev));
bbf9d17d
GR
479 }
480
481 /*
482 * Get MMIO regions for the device from the MC:
483 *
484 * NOTE: the root DPRC is a special case as its MMIO region is
485 * obtained from the device tree
486 */
487 if (parent_mc_dev && obj_desc->region_count != 0) {
488 error = fsl_mc_device_get_mmio_regions(mc_dev,
489 parent_mc_dev);
490 if (error < 0)
491 goto error_cleanup_dev;
492 }
493
494 /*
495 * The device-specific probe callback will get invoked by device_add()
496 */
497 error = device_add(&mc_dev->dev);
498 if (error < 0) {
499 dev_err(parent_dev,
500 "device_add() failed for device %s: %d\n",
501 dev_name(&mc_dev->dev), error);
502 goto error_cleanup_dev;
503 }
504
505 (void)get_device(&mc_dev->dev);
506 dev_dbg(parent_dev, "Added MC object device %s\n",
507 dev_name(&mc_dev->dev));
508
509 *new_mc_dev = mc_dev;
510 return 0;
511
512error_cleanup_dev:
513 kfree(mc_dev->regions);
514 if (mc_bus)
515 devm_kfree(parent_dev, mc_bus);
516 else
517 kmem_cache_free(mc_dev_cache, mc_dev);
518
519 return error;
520}
521EXPORT_SYMBOL_GPL(fsl_mc_device_add);
522
523/**
524 * fsl_mc_device_remove - Remove a MC object device from being visible to
525 * Linux
526 *
527 * @mc_dev: Pointer to a MC object device object
528 */
529void fsl_mc_device_remove(struct fsl_mc_device *mc_dev)
530{
531 struct fsl_mc_bus *mc_bus = NULL;
532
533 kfree(mc_dev->regions);
534
535 /*
536 * The device-specific remove callback will get invoked by device_del()
537 */
538 device_del(&mc_dev->dev);
539 put_device(&mc_dev->dev);
540
541 if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) {
bbf9d17d 542 mc_bus = to_fsl_mc_bus(mc_dev);
2bdc55d9
GR
543 if (mc_dev->mc_io) {
544 fsl_destroy_mc_io(mc_dev->mc_io);
545 mc_dev->mc_io = NULL;
546 }
547
565b9450 548 if (fsl_mc_is_root_dprc(&mc_dev->dev)) {
565b9450
IK
549 if (atomic_read(&root_dprc_count) > 0)
550 atomic_dec(&root_dprc_count);
551 else
552 WARN_ON(1);
553 }
bbf9d17d
GR
554 }
555
bbf9d17d
GR
556 if (mc_bus)
557 devm_kfree(mc_dev->dev.parent, mc_bus);
558 else
559 kmem_cache_free(mc_dev_cache, mc_dev);
560}
561EXPORT_SYMBOL_GPL(fsl_mc_device_remove);
562
563static int parse_mc_ranges(struct device *dev,
564 int *paddr_cells,
565 int *mc_addr_cells,
566 int *mc_size_cells,
567 const __be32 **ranges_start,
ba72f25b 568 u8 *num_ranges)
bbf9d17d
GR
569{
570 const __be32 *prop;
571 int range_tuple_cell_count;
572 int ranges_len;
573 int tuple_len;
574 struct device_node *mc_node = dev->of_node;
575
576 *ranges_start = of_get_property(mc_node, "ranges", &ranges_len);
577 if (!(*ranges_start) || !ranges_len) {
578 dev_warn(dev,
579 "missing or empty ranges property for device tree node '%s'\n",
580 mc_node->name);
581
582 *num_ranges = 0;
583 return 0;
584 }
585
586 *paddr_cells = of_n_addr_cells(mc_node);
587
588 prop = of_get_property(mc_node, "#address-cells", NULL);
589 if (prop)
590 *mc_addr_cells = be32_to_cpup(prop);
591 else
592 *mc_addr_cells = *paddr_cells;
593
594 prop = of_get_property(mc_node, "#size-cells", NULL);
595 if (prop)
596 *mc_size_cells = be32_to_cpup(prop);
597 else
598 *mc_size_cells = of_n_size_cells(mc_node);
599
600 range_tuple_cell_count = *paddr_cells + *mc_addr_cells +
601 *mc_size_cells;
602
603 tuple_len = range_tuple_cell_count * sizeof(__be32);
604 if (ranges_len % tuple_len != 0) {
605 dev_err(dev, "malformed ranges property '%s'\n", mc_node->name);
606 return -EINVAL;
607 }
608
609 *num_ranges = ranges_len / tuple_len;
610 return 0;
611}
612
613static int get_mc_addr_translation_ranges(struct device *dev,
614 struct fsl_mc_addr_translation_range
615 **ranges,
ba72f25b 616 u8 *num_ranges)
bbf9d17d
GR
617{
618 int error;
619 int paddr_cells;
620 int mc_addr_cells;
621 int mc_size_cells;
622 int i;
623 const __be32 *ranges_start;
624 const __be32 *cell;
625
626 error = parse_mc_ranges(dev,
627 &paddr_cells,
628 &mc_addr_cells,
629 &mc_size_cells,
630 &ranges_start,
631 num_ranges);
632 if (error < 0)
633 return error;
634
635 if (!(*num_ranges)) {
636 /*
637 * Missing or empty ranges property ("ranges;") for the
638 * 'fsl,qoriq-mc' node. In this case, identity mapping
639 * will be used.
640 */
641 *ranges = NULL;
642 return 0;
643 }
644
645 *ranges = devm_kcalloc(dev, *num_ranges,
646 sizeof(struct fsl_mc_addr_translation_range),
647 GFP_KERNEL);
648 if (!(*ranges))
649 return -ENOMEM;
650
651 cell = ranges_start;
652 for (i = 0; i < *num_ranges; ++i) {
653 struct fsl_mc_addr_translation_range *range = &(*ranges)[i];
654
1ee695fa
GR
655 range->mc_region_type = of_read_number(cell, 1);
656 range->start_mc_offset = of_read_number(cell + 1,
657 mc_addr_cells - 1);
bbf9d17d
GR
658 cell += mc_addr_cells;
659 range->start_phys_addr = of_read_number(cell, paddr_cells);
660 cell += paddr_cells;
1ee695fa 661 range->end_mc_offset = range->start_mc_offset +
bbf9d17d
GR
662 of_read_number(cell, mc_size_cells);
663
664 cell += mc_size_cells;
665 }
666
667 return 0;
668}
669
670/**
671 * fsl_mc_bus_probe - callback invoked when the root MC bus is being
672 * added
673 */
674static int fsl_mc_bus_probe(struct platform_device *pdev)
675{
676 struct dprc_obj_desc obj_desc;
677 int error;
678 struct fsl_mc *mc;
679 struct fsl_mc_device *mc_bus_dev = NULL;
680 struct fsl_mc_io *mc_io = NULL;
681 int container_id;
682 phys_addr_t mc_portal_phys_addr;
a2f9ff6c 683 u32 mc_portal_size;
bbf9d17d
GR
684 struct mc_version mc_version;
685 struct resource res;
686
687 dev_info(&pdev->dev, "Root MC bus device probed");
688
689 mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL);
690 if (!mc)
691 return -ENOMEM;
692
693 platform_set_drvdata(pdev, mc);
694
695 /*
696 * Get physical address of MC portal for the root DPRC:
697 */
698 error = of_address_to_resource(pdev->dev.of_node, 0, &res);
699 if (error < 0) {
700 dev_err(&pdev->dev,
701 "of_address_to_resource() failed for %s\n",
702 pdev->dev.of_node->full_name);
703 return error;
704 }
705
706 mc_portal_phys_addr = res.start;
707 mc_portal_size = resource_size(&res);
708 error = fsl_create_mc_io(&pdev->dev, mc_portal_phys_addr,
1129cde5
GR
709 mc_portal_size, NULL,
710 FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, &mc_io);
bbf9d17d
GR
711 if (error < 0)
712 return error;
713
40577435 714 error = mc_get_version(mc_io, 0, &mc_version);
bbf9d17d
GR
715 if (error != 0) {
716 dev_err(&pdev->dev,
717 "mc_get_version() failed with error %d\n", error);
718 goto error_cleanup_mc_io;
719 }
720
721 dev_info(&pdev->dev,
722 "Freescale Management Complex Firmware version: %u.%u.%u\n",
723 mc_version.major, mc_version.minor, mc_version.revision);
724
725 if (mc_version.major < MC_VER_MAJOR) {
726 dev_err(&pdev->dev,
727 "ERROR: MC firmware version not supported by driver (driver version: %u.%u)\n",
728 MC_VER_MAJOR, MC_VER_MINOR);
729 error = -ENOTSUPP;
730 goto error_cleanup_mc_io;
731 }
732
733 if (mc_version.major > MC_VER_MAJOR) {
734 dev_warn(&pdev->dev,
735 "WARNING: driver may not support newer MC firmware features (driver version: %u.%u)\n",
736 MC_VER_MAJOR, MC_VER_MINOR);
737 }
738
739 error = get_mc_addr_translation_ranges(&pdev->dev,
740 &mc->translation_ranges,
741 &mc->num_translation_ranges);
742 if (error < 0)
743 goto error_cleanup_mc_io;
744
40577435 745 error = dpmng_get_container_id(mc_io, 0, &container_id);
bbf9d17d
GR
746 if (error < 0) {
747 dev_err(&pdev->dev,
748 "dpmng_get_container_id() failed: %d\n", error);
749 goto error_cleanup_mc_io;
750 }
751
752 obj_desc.vendor = FSL_MC_VENDOR_FREESCALE;
753 strcpy(obj_desc.type, "dprc");
754 obj_desc.id = container_id;
755 obj_desc.ver_major = DPRC_VER_MAJOR;
756 obj_desc.ver_minor = DPRC_VER_MINOR;
a17f4aa6 757 obj_desc.irq_count = 1;
bbf9d17d
GR
758 obj_desc.region_count = 0;
759
760 error = fsl_mc_device_add(&obj_desc, mc_io, &pdev->dev, &mc_bus_dev);
761 if (error < 0)
762 goto error_cleanup_mc_io;
763
764 mc->root_mc_bus_dev = mc_bus_dev;
765 return 0;
766
767error_cleanup_mc_io:
768 fsl_destroy_mc_io(mc_io);
769 return error;
770}
771
772/**
773 * fsl_mc_bus_remove - callback invoked when the root MC bus is being
774 * removed
775 */
776static int fsl_mc_bus_remove(struct platform_device *pdev)
777{
778 struct fsl_mc *mc = platform_get_drvdata(pdev);
779
b55f00c6 780 if (WARN_ON(!fsl_mc_is_root_dprc(&mc->root_mc_bus_dev->dev)))
bbf9d17d
GR
781 return -EINVAL;
782
783 fsl_mc_device_remove(mc->root_mc_bus_dev);
784 dev_info(&pdev->dev, "Root MC bus device removed");
785 return 0;
786}
787
788static const struct of_device_id fsl_mc_bus_match_table[] = {
789 {.compatible = "fsl,qoriq-mc",},
790 {},
791};
792
793MODULE_DEVICE_TABLE(of, fsl_mc_bus_match_table);
794
795static struct platform_driver fsl_mc_bus_driver = {
796 .driver = {
797 .name = "fsl_mc_bus",
bbf9d17d
GR
798 .pm = NULL,
799 .of_match_table = fsl_mc_bus_match_table,
800 },
801 .probe = fsl_mc_bus_probe,
802 .remove = fsl_mc_bus_remove,
803};
804
805static int __init fsl_mc_bus_driver_init(void)
806{
807 int error;
808
809 mc_dev_cache = kmem_cache_create("fsl_mc_device",
810 sizeof(struct fsl_mc_device), 0, 0,
811 NULL);
812 if (!mc_dev_cache) {
813 pr_err("Could not create fsl_mc_device cache\n");
814 return -ENOMEM;
815 }
816
817 error = bus_register(&fsl_mc_bus_type);
818 if (error < 0) {
819 pr_err("fsl-mc bus type registration failed: %d\n", error);
820 goto error_cleanup_cache;
821 }
822
823 pr_info("fsl-mc bus type registered\n");
824
825 error = platform_driver_register(&fsl_mc_bus_driver);
826 if (error < 0) {
827 pr_err("platform_driver_register() failed: %d\n", error);
828 goto error_cleanup_bus;
829 }
830
f2f2726b
GR
831 error = dprc_driver_init();
832 if (error < 0)
833 goto error_cleanup_driver;
834
e91ffa9e
GR
835 error = fsl_mc_allocator_driver_init();
836 if (error < 0)
837 goto error_cleanup_dprc_driver;
838
660a24bf
GR
839 error = its_fsl_mc_msi_init();
840 if (error < 0)
841 goto error_cleanup_mc_allocator;
842
bbf9d17d
GR
843 return 0;
844
660a24bf
GR
845error_cleanup_mc_allocator:
846 fsl_mc_allocator_driver_exit();
847
e91ffa9e
GR
848error_cleanup_dprc_driver:
849 dprc_driver_exit();
850
f2f2726b
GR
851error_cleanup_driver:
852 platform_driver_unregister(&fsl_mc_bus_driver);
853
bbf9d17d
GR
854error_cleanup_bus:
855 bus_unregister(&fsl_mc_bus_type);
856
857error_cleanup_cache:
858 kmem_cache_destroy(mc_dev_cache);
859 return error;
860}
861
862postcore_initcall(fsl_mc_bus_driver_init);
863
864static void __exit fsl_mc_bus_driver_exit(void)
865{
866 if (WARN_ON(!mc_dev_cache))
867 return;
868
660a24bf 869 its_fsl_mc_msi_cleanup();
e91ffa9e 870 fsl_mc_allocator_driver_exit();
f2f2726b 871 dprc_driver_exit();
bbf9d17d
GR
872 platform_driver_unregister(&fsl_mc_bus_driver);
873 bus_unregister(&fsl_mc_bus_type);
874 kmem_cache_destroy(mc_dev_cache);
875 pr_info("MC bus unregistered\n");
876}
877
878module_exit(fsl_mc_bus_driver_exit);
879
880MODULE_AUTHOR("Freescale Semiconductor Inc.");
881MODULE_DESCRIPTION("Freescale Management Complex (MC) bus driver");
882MODULE_LICENSE("GPL");