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