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