2 * Freescale Management Complex (MC) bus driver
4 * Copyright (C) 2014 Freescale Semiconductor, Inc.
5 * Author: German Rivera <German.Rivera@freescale.com>
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.
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>
19 #include "../include/dpmng.h"
20 #include "../include/mc-sys.h"
23 static struct kmem_cache
*mc_dev_cache
;
26 * fsl_mc_bus_match - device to driver matching callback
27 * @dev: the MC object device structure to match against
28 * @drv: the device driver to search for matching MC object device id
31 * Returns 1 on success, 0 otherwise.
33 static int fsl_mc_bus_match(struct device
*dev
, struct device_driver
*drv
)
35 const struct fsl_mc_device_match_id
*id
;
36 struct fsl_mc_device
*mc_dev
= to_fsl_mc_device(dev
);
37 struct fsl_mc_driver
*mc_drv
= to_fsl_mc_driver(drv
);
40 if (WARN_ON(!fsl_mc_bus_type
.dev_root
))
43 if (!mc_drv
->match_id_table
)
47 * If the object is not 'plugged' don't match.
48 * Only exception is the root DPRC, which is a special case.
50 if ((mc_dev
->obj_desc
.state
& DPRC_OBJ_STATE_PLUGGED
) == 0 &&
51 &mc_dev
->dev
!= fsl_mc_bus_type
.dev_root
)
55 * Traverse the match_id table of the given driver, trying to find
56 * a matching for the given MC object device.
58 for (id
= mc_drv
->match_id_table
; id
->vendor
!= 0x0; id
++) {
59 if (id
->vendor
== mc_dev
->obj_desc
.vendor
&&
60 strcmp(id
->obj_type
, mc_dev
->obj_desc
.type
) == 0 &&
61 id
->ver_major
== mc_dev
->obj_desc
.ver_major
&&
62 id
->ver_minor
== mc_dev
->obj_desc
.ver_minor
) {
69 dev_dbg(dev
, "%smatched\n", found
? "" : "not ");
74 * fsl_mc_bus_uevent - callback invoked when a device is added
76 static int fsl_mc_bus_uevent(struct device
*dev
, struct kobj_uevent_env
*env
)
78 pr_debug("%s invoked\n", __func__
);
82 struct bus_type fsl_mc_bus_type
= {
84 .match
= fsl_mc_bus_match
,
85 .uevent
= fsl_mc_bus_uevent
,
87 EXPORT_SYMBOL_GPL(fsl_mc_bus_type
);
89 static int fsl_mc_driver_probe(struct device
*dev
)
91 struct fsl_mc_driver
*mc_drv
;
92 struct fsl_mc_device
*mc_dev
= to_fsl_mc_device(dev
);
95 if (WARN_ON(!dev
->driver
))
98 mc_drv
= to_fsl_mc_driver(dev
->driver
);
99 if (WARN_ON(!mc_drv
->probe
))
102 error
= mc_drv
->probe(mc_dev
);
104 dev_err(dev
, "MC object device probe callback failed: %d\n",
112 static int fsl_mc_driver_remove(struct device
*dev
)
114 struct fsl_mc_driver
*mc_drv
= to_fsl_mc_driver(dev
->driver
);
115 struct fsl_mc_device
*mc_dev
= to_fsl_mc_device(dev
);
118 if (WARN_ON(!dev
->driver
))
121 error
= mc_drv
->remove(mc_dev
);
124 "MC object device remove callback failed: %d\n",
132 static void fsl_mc_driver_shutdown(struct device
*dev
)
134 struct fsl_mc_driver
*mc_drv
= to_fsl_mc_driver(dev
->driver
);
135 struct fsl_mc_device
*mc_dev
= to_fsl_mc_device(dev
);
137 mc_drv
->shutdown(mc_dev
);
141 * __fsl_mc_driver_register - registers a child device driver with the
144 * This function is implicitly invoked from the registration function of
145 * fsl_mc device drivers, which is generated by the
146 * module_fsl_mc_driver() macro.
148 int __fsl_mc_driver_register(struct fsl_mc_driver
*mc_driver
,
149 struct module
*owner
)
153 mc_driver
->driver
.owner
= owner
;
154 mc_driver
->driver
.bus
= &fsl_mc_bus_type
;
156 if (mc_driver
->probe
)
157 mc_driver
->driver
.probe
= fsl_mc_driver_probe
;
159 if (mc_driver
->remove
)
160 mc_driver
->driver
.remove
= fsl_mc_driver_remove
;
162 if (mc_driver
->shutdown
)
163 mc_driver
->driver
.shutdown
= fsl_mc_driver_shutdown
;
165 error
= driver_register(&mc_driver
->driver
);
167 pr_err("driver_register() failed for %s: %d\n",
168 mc_driver
->driver
.name
, error
);
172 pr_info("MC object device driver %s registered\n",
173 mc_driver
->driver
.name
);
176 EXPORT_SYMBOL_GPL(__fsl_mc_driver_register
);
179 * fsl_mc_driver_unregister - unregisters a device driver from the
182 void fsl_mc_driver_unregister(struct fsl_mc_driver
*mc_driver
)
184 driver_unregister(&mc_driver
->driver
);
186 EXPORT_SYMBOL_GPL(fsl_mc_driver_unregister
);
188 static int get_dprc_icid(struct fsl_mc_io
*mc_io
,
189 int container_id
, uint16_t *icid
)
191 uint16_t dprc_handle
;
192 struct dprc_attributes attr
;
195 error
= dprc_open(mc_io
, container_id
, &dprc_handle
);
197 pr_err("dprc_open() failed: %d\n", error
);
201 memset(&attr
, 0, sizeof(attr
));
202 error
= dprc_get_attributes(mc_io
, dprc_handle
, &attr
);
204 pr_err("dprc_get_attributes() failed: %d\n", error
);
212 (void)dprc_close(mc_io
, dprc_handle
);
216 static int translate_mc_addr(uint64_t mc_addr
, phys_addr_t
*phys_addr
)
219 struct fsl_mc
*mc
= dev_get_drvdata(fsl_mc_bus_type
.dev_root
->parent
);
221 if (mc
->num_translation_ranges
== 0) {
223 * Do identity mapping:
225 *phys_addr
= mc_addr
;
229 for (i
= 0; i
< mc
->num_translation_ranges
; i
++) {
230 struct fsl_mc_addr_translation_range
*range
=
231 &mc
->translation_ranges
[i
];
233 if (mc_addr
>= range
->start_mc_addr
&&
234 mc_addr
< range
->end_mc_addr
) {
235 *phys_addr
= range
->start_phys_addr
+
236 (mc_addr
- range
->start_mc_addr
);
244 static int fsl_mc_device_get_mmio_regions(struct fsl_mc_device
*mc_dev
,
245 struct fsl_mc_device
*mc_bus_dev
)
249 struct resource
*regions
;
250 struct dprc_obj_desc
*obj_desc
= &mc_dev
->obj_desc
;
251 struct device
*parent_dev
= mc_dev
->dev
.parent
;
253 regions
= kmalloc_array(obj_desc
->region_count
,
254 sizeof(regions
[0]), GFP_KERNEL
);
258 for (i
= 0; i
< obj_desc
->region_count
; i
++) {
259 struct dprc_region_desc region_desc
;
261 error
= dprc_get_obj_region(mc_bus_dev
->mc_io
,
262 mc_bus_dev
->mc_handle
,
264 obj_desc
->id
, i
, ®ion_desc
);
267 "dprc_get_obj_region() failed: %d\n", error
);
268 goto error_cleanup_regions
;
271 WARN_ON(region_desc
.base_paddr
== 0x0);
272 WARN_ON(region_desc
.size
== 0);
273 error
= translate_mc_addr(region_desc
.base_paddr
,
277 "Invalid MC address: %#llx\n",
278 region_desc
.base_paddr
);
279 goto error_cleanup_regions
;
282 regions
[i
].end
= regions
[i
].start
+ region_desc
.size
- 1;
283 regions
[i
].name
= "fsl-mc object MMIO region";
284 regions
[i
].flags
= IORESOURCE_IO
;
287 mc_dev
->regions
= regions
;
290 error_cleanup_regions
:
296 * Add a newly discovered MC object device to be visible in Linux
298 int fsl_mc_device_add(struct dprc_obj_desc
*obj_desc
,
299 struct fsl_mc_io
*mc_io
,
300 struct device
*parent_dev
,
301 struct fsl_mc_device
**new_mc_dev
)
304 struct fsl_mc_device
*mc_dev
= NULL
;
305 struct fsl_mc_bus
*mc_bus
= NULL
;
306 struct fsl_mc_device
*parent_mc_dev
;
308 if (parent_dev
->bus
== &fsl_mc_bus_type
)
309 parent_mc_dev
= to_fsl_mc_device(parent_dev
);
311 parent_mc_dev
= NULL
;
313 if (strcmp(obj_desc
->type
, "dprc") == 0) {
315 * Allocate an MC bus device object:
317 mc_bus
= devm_kzalloc(parent_dev
, sizeof(*mc_bus
), GFP_KERNEL
);
321 mc_dev
= &mc_bus
->mc_dev
;
324 * Allocate a regular fsl_mc_device object:
326 mc_dev
= kmem_cache_zalloc(mc_dev_cache
, GFP_KERNEL
);
331 mc_dev
->obj_desc
= *obj_desc
;
332 mc_dev
->mc_io
= mc_io
;
333 device_initialize(&mc_dev
->dev
);
334 mc_dev
->dev
.parent
= parent_dev
;
335 mc_dev
->dev
.bus
= &fsl_mc_bus_type
;
336 dev_set_name(&mc_dev
->dev
, "%s.%d", obj_desc
->type
, obj_desc
->id
);
338 if (strcmp(obj_desc
->type
, "dprc") == 0) {
339 struct fsl_mc_io
*mc_io2
;
341 mc_dev
->flags
|= FSL_MC_IS_DPRC
;
344 * To get the DPRC's ICID, we need to open the DPRC
345 * in get_dprc_icid(). For child DPRCs, we do so using the
346 * parent DPRC's MC portal instead of the child DPRC's MC
347 * portal, in case the child DPRC is already opened with
348 * its own portal (e.g., the DPRC used by AIOP).
350 * NOTE: There cannot be more than one active open for a
351 * given MC object, using the same MC portal.
355 * device being added is a child DPRC device
357 mc_io2
= parent_mc_dev
->mc_io
;
360 * device being added is the root DPRC device
362 if (WARN_ON(!mc_io
)) {
364 goto error_cleanup_dev
;
369 if (!fsl_mc_bus_type
.dev_root
)
370 fsl_mc_bus_type
.dev_root
= &mc_dev
->dev
;
373 error
= get_dprc_icid(mc_io2
, obj_desc
->id
, &mc_dev
->icid
);
375 goto error_cleanup_dev
;
378 * A non-DPRC MC object device has to be a child of another
379 * MC object (specifically a DPRC object)
381 mc_dev
->icid
= parent_mc_dev
->icid
;
382 mc_dev
->dma_mask
= FSL_MC_DEFAULT_DMA_MASK
;
383 mc_dev
->dev
.dma_mask
= &mc_dev
->dma_mask
;
387 * Get MMIO regions for the device from the MC:
389 * NOTE: the root DPRC is a special case as its MMIO region is
390 * obtained from the device tree
392 if (parent_mc_dev
&& obj_desc
->region_count
!= 0) {
393 error
= fsl_mc_device_get_mmio_regions(mc_dev
,
396 goto error_cleanup_dev
;
400 * The device-specific probe callback will get invoked by device_add()
402 error
= device_add(&mc_dev
->dev
);
405 "device_add() failed for device %s: %d\n",
406 dev_name(&mc_dev
->dev
), error
);
407 goto error_cleanup_dev
;
410 (void)get_device(&mc_dev
->dev
);
411 dev_dbg(parent_dev
, "Added MC object device %s\n",
412 dev_name(&mc_dev
->dev
));
414 *new_mc_dev
= mc_dev
;
418 kfree(mc_dev
->regions
);
420 devm_kfree(parent_dev
, mc_bus
);
422 kmem_cache_free(mc_dev_cache
, mc_dev
);
426 EXPORT_SYMBOL_GPL(fsl_mc_device_add
);
429 * fsl_mc_device_remove - Remove a MC object device from being visible to
432 * @mc_dev: Pointer to a MC object device object
434 void fsl_mc_device_remove(struct fsl_mc_device
*mc_dev
)
436 struct fsl_mc_bus
*mc_bus
= NULL
;
438 kfree(mc_dev
->regions
);
441 * The device-specific remove callback will get invoked by device_del()
443 device_del(&mc_dev
->dev
);
444 put_device(&mc_dev
->dev
);
446 if (strcmp(mc_dev
->obj_desc
.type
, "dprc") == 0) {
447 mc_bus
= to_fsl_mc_bus(mc_dev
);
449 fsl_destroy_mc_io(mc_dev
->mc_io
);
450 mc_dev
->mc_io
= NULL
;
453 if (&mc_dev
->dev
== fsl_mc_bus_type
.dev_root
)
454 fsl_mc_bus_type
.dev_root
= NULL
;
458 devm_kfree(mc_dev
->dev
.parent
, mc_bus
);
460 kmem_cache_free(mc_dev_cache
, mc_dev
);
462 EXPORT_SYMBOL_GPL(fsl_mc_device_remove
);
464 static int parse_mc_ranges(struct device
*dev
,
468 const __be32
**ranges_start
,
472 int range_tuple_cell_count
;
475 struct device_node
*mc_node
= dev
->of_node
;
477 *ranges_start
= of_get_property(mc_node
, "ranges", &ranges_len
);
478 if (!(*ranges_start
) || !ranges_len
) {
480 "missing or empty ranges property for device tree node '%s'\n",
487 *paddr_cells
= of_n_addr_cells(mc_node
);
489 prop
= of_get_property(mc_node
, "#address-cells", NULL
);
491 *mc_addr_cells
= be32_to_cpup(prop
);
493 *mc_addr_cells
= *paddr_cells
;
495 prop
= of_get_property(mc_node
, "#size-cells", NULL
);
497 *mc_size_cells
= be32_to_cpup(prop
);
499 *mc_size_cells
= of_n_size_cells(mc_node
);
501 range_tuple_cell_count
= *paddr_cells
+ *mc_addr_cells
+
504 tuple_len
= range_tuple_cell_count
* sizeof(__be32
);
505 if (ranges_len
% tuple_len
!= 0) {
506 dev_err(dev
, "malformed ranges property '%s'\n", mc_node
->name
);
510 *num_ranges
= ranges_len
/ tuple_len
;
514 static int get_mc_addr_translation_ranges(struct device
*dev
,
515 struct fsl_mc_addr_translation_range
524 const __be32
*ranges_start
;
527 error
= parse_mc_ranges(dev
,
536 if (!(*num_ranges
)) {
538 * Missing or empty ranges property ("ranges;") for the
539 * 'fsl,qoriq-mc' node. In this case, identity mapping
546 *ranges
= devm_kcalloc(dev
, *num_ranges
,
547 sizeof(struct fsl_mc_addr_translation_range
),
553 for (i
= 0; i
< *num_ranges
; ++i
) {
554 struct fsl_mc_addr_translation_range
*range
= &(*ranges
)[i
];
556 range
->start_mc_addr
= of_read_number(cell
, mc_addr_cells
);
557 cell
+= mc_addr_cells
;
558 range
->start_phys_addr
= of_read_number(cell
, paddr_cells
);
560 range
->end_mc_addr
= range
->start_mc_addr
+
561 of_read_number(cell
, mc_size_cells
);
563 cell
+= mc_size_cells
;
570 * fsl_mc_bus_probe - callback invoked when the root MC bus is being
573 static int fsl_mc_bus_probe(struct platform_device
*pdev
)
575 struct dprc_obj_desc obj_desc
;
578 struct fsl_mc_device
*mc_bus_dev
= NULL
;
579 struct fsl_mc_io
*mc_io
= NULL
;
581 phys_addr_t mc_portal_phys_addr
;
582 uint32_t mc_portal_size
;
583 struct mc_version mc_version
;
586 dev_info(&pdev
->dev
, "Root MC bus device probed");
588 mc
= devm_kzalloc(&pdev
->dev
, sizeof(*mc
), GFP_KERNEL
);
592 platform_set_drvdata(pdev
, mc
);
595 * Get physical address of MC portal for the root DPRC:
597 error
= of_address_to_resource(pdev
->dev
.of_node
, 0, &res
);
600 "of_address_to_resource() failed for %s\n",
601 pdev
->dev
.of_node
->full_name
);
605 mc_portal_phys_addr
= res
.start
;
606 mc_portal_size
= resource_size(&res
);
607 error
= fsl_create_mc_io(&pdev
->dev
, mc_portal_phys_addr
,
608 mc_portal_size
, NULL
, 0, &mc_io
);
612 error
= mc_get_version(mc_io
, &mc_version
);
615 "mc_get_version() failed with error %d\n", error
);
616 goto error_cleanup_mc_io
;
620 "Freescale Management Complex Firmware version: %u.%u.%u\n",
621 mc_version
.major
, mc_version
.minor
, mc_version
.revision
);
623 if (mc_version
.major
< MC_VER_MAJOR
) {
625 "ERROR: MC firmware version not supported by driver (driver version: %u.%u)\n",
626 MC_VER_MAJOR
, MC_VER_MINOR
);
628 goto error_cleanup_mc_io
;
631 if (mc_version
.major
> MC_VER_MAJOR
) {
633 "WARNING: driver may not support newer MC firmware features (driver version: %u.%u)\n",
634 MC_VER_MAJOR
, MC_VER_MINOR
);
637 error
= get_mc_addr_translation_ranges(&pdev
->dev
,
638 &mc
->translation_ranges
,
639 &mc
->num_translation_ranges
);
641 goto error_cleanup_mc_io
;
643 error
= dpmng_get_container_id(mc_io
, &container_id
);
646 "dpmng_get_container_id() failed: %d\n", error
);
647 goto error_cleanup_mc_io
;
650 obj_desc
.vendor
= FSL_MC_VENDOR_FREESCALE
;
651 strcpy(obj_desc
.type
, "dprc");
652 obj_desc
.id
= container_id
;
653 obj_desc
.ver_major
= DPRC_VER_MAJOR
;
654 obj_desc
.ver_minor
= DPRC_VER_MINOR
;
655 obj_desc
.region_count
= 0;
657 error
= fsl_mc_device_add(&obj_desc
, mc_io
, &pdev
->dev
, &mc_bus_dev
);
659 goto error_cleanup_mc_io
;
661 mc
->root_mc_bus_dev
= mc_bus_dev
;
665 fsl_destroy_mc_io(mc_io
);
670 * fsl_mc_bus_remove - callback invoked when the root MC bus is being
673 static int fsl_mc_bus_remove(struct platform_device
*pdev
)
675 struct fsl_mc
*mc
= platform_get_drvdata(pdev
);
677 if (WARN_ON(&mc
->root_mc_bus_dev
->dev
!= fsl_mc_bus_type
.dev_root
))
680 fsl_mc_device_remove(mc
->root_mc_bus_dev
);
681 dev_info(&pdev
->dev
, "Root MC bus device removed");
685 static const struct of_device_id fsl_mc_bus_match_table
[] = {
686 {.compatible
= "fsl,qoriq-mc",},
690 MODULE_DEVICE_TABLE(of
, fsl_mc_bus_match_table
);
692 static struct platform_driver fsl_mc_bus_driver
= {
694 .name
= "fsl_mc_bus",
695 .owner
= THIS_MODULE
,
697 .of_match_table
= fsl_mc_bus_match_table
,
699 .probe
= fsl_mc_bus_probe
,
700 .remove
= fsl_mc_bus_remove
,
703 static int __init
fsl_mc_bus_driver_init(void)
707 mc_dev_cache
= kmem_cache_create("fsl_mc_device",
708 sizeof(struct fsl_mc_device
), 0, 0,
711 pr_err("Could not create fsl_mc_device cache\n");
715 error
= bus_register(&fsl_mc_bus_type
);
717 pr_err("fsl-mc bus type registration failed: %d\n", error
);
718 goto error_cleanup_cache
;
721 pr_info("fsl-mc bus type registered\n");
723 error
= platform_driver_register(&fsl_mc_bus_driver
);
725 pr_err("platform_driver_register() failed: %d\n", error
);
726 goto error_cleanup_bus
;
729 error
= dprc_driver_init();
731 goto error_cleanup_driver
;
733 error
= fsl_mc_allocator_driver_init();
735 goto error_cleanup_dprc_driver
;
739 error_cleanup_dprc_driver
:
742 error_cleanup_driver
:
743 platform_driver_unregister(&fsl_mc_bus_driver
);
746 bus_unregister(&fsl_mc_bus_type
);
749 kmem_cache_destroy(mc_dev_cache
);
753 postcore_initcall(fsl_mc_bus_driver_init
);
755 static void __exit
fsl_mc_bus_driver_exit(void)
757 if (WARN_ON(!mc_dev_cache
))
760 fsl_mc_allocator_driver_exit();
762 platform_driver_unregister(&fsl_mc_bus_driver
);
763 bus_unregister(&fsl_mc_bus_type
);
764 kmem_cache_destroy(mc_dev_cache
);
765 pr_info("MC bus unregistered\n");
768 module_exit(fsl_mc_bus_driver_exit
);
770 MODULE_AUTHOR("Freescale Semiconductor Inc.");
771 MODULE_DESCRIPTION("Freescale Management Complex (MC) bus driver");
772 MODULE_LICENSE("GPL");