diff options
Diffstat (limited to 'drivers/of/platform.c')
| -rw-r--r-- | drivers/of/platform.c | 223 | 
1 files changed, 147 insertions, 76 deletions
diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 9b439ac63d8..500436f9be7 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -21,7 +21,6 @@  #include <linux/of_device.h>  #include <linux/of_irq.h>  #include <linux/of_platform.h> -#include <linux/of_reserved_mem.h>  #include <linux/platform_device.h>  const struct of_device_id of_default_bus_match_table[] = { @@ -52,10 +51,6 @@ struct platform_device *of_find_device_by_node(struct device_node *np)  }  EXPORT_SYMBOL(of_find_device_by_node); -#if defined(CONFIG_PPC_DCR) -#include <asm/dcr.h> -#endif -  #ifdef CONFIG_OF_ADDRESS  /*   * The following routines scan a subtree and registers a device for @@ -69,66 +64,35 @@ EXPORT_SYMBOL(of_find_device_by_node);   * of_device_make_bus_id - Use the device node data to assign a unique name   * @dev: pointer to device structure that is linked to a device tree node   * - * This routine will first try using either the dcr-reg or the reg property - * value to derive a unique name.  As a last resort it will use the node - * name followed by a unique number. + * This routine will first try using the translated bus address to + * derive a unique name. If it cannot, then it will prepend names from + * parent nodes until a unique name can be derived.   */  void of_device_make_bus_id(struct device *dev)  { -	static atomic_t bus_no_reg_magic;  	struct device_node *node = dev->of_node;  	const __be32 *reg;  	u64 addr; -	const __be32 *addrp; -	int magic; -#ifdef CONFIG_PPC_DCR -	/* -	 * If it's a DCR based device, use 'd' for native DCRs -	 * and 'D' for MMIO DCRs. -	 */ -	reg = of_get_property(node, "dcr-reg", NULL); -	if (reg) { -#ifdef CONFIG_PPC_DCR_NATIVE -		dev_set_name(dev, "d%x.%s", *reg, node->name); -#else /* CONFIG_PPC_DCR_NATIVE */ -		u64 addr = of_translate_dcr_address(node, *reg, NULL); -		if (addr != OF_BAD_ADDR) { -			dev_set_name(dev, "D%llx.%s", -				     (unsigned long long)addr, node->name); +	/* Construct the name, using parent nodes if necessary to ensure uniqueness */ +	while (node->parent) { +		/* +		 * If the address can be translated, then that is as much +		 * uniqueness as we need. Make it the first component and return +		 */ +		reg = of_get_property(node, "reg", NULL); +		if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) { +			dev_set_name(dev, dev_name(dev) ? "%llx.%s:%s" : "%llx.%s", +				     (unsigned long long)addr, node->name, +				     dev_name(dev));  			return;  		} -#endif /* !CONFIG_PPC_DCR_NATIVE */ -	} -#endif /* CONFIG_PPC_DCR */ -	/* -	 * For MMIO, get the physical address -	 */ -	reg = of_get_property(node, "reg", NULL); -	if (reg) { -		if (of_can_translate_address(node)) { -			addr = of_translate_address(node, reg); -		} else { -			addrp = of_get_address(node, 0, NULL, NULL); -			if (addrp) -				addr = of_read_number(addrp, 1); -			else -				addr = OF_BAD_ADDR; -		} -		if (addr != OF_BAD_ADDR) { -			dev_set_name(dev, "%llx.%s", -				     (unsigned long long)addr, node->name); -			return; -		} +		/* format arguments only used if dev_name() resolves to NULL */ +		dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s", +			     strrchr(node->full_name, '/') + 1, dev_name(dev)); +		node = node->parent;  	} - -	/* -	 * No BusID, use the node name and add a globally incremented -	 * counter (and pray...) -	 */ -	magic = atomic_add_return(1, &bus_no_reg_magic); -	dev_set_name(dev, "%s.%d", node->name, magic - 1);  }  /** @@ -150,9 +114,8 @@ struct platform_device *of_device_alloc(struct device_node *np,  		return NULL;  	/* count the io and irq resources */ -	if (of_can_translate_address(np)) -		while (of_address_to_resource(np, num_reg, &temp_res) == 0) -			num_reg++; +	while (of_address_to_resource(np, num_reg, &temp_res) == 0) +		num_reg++;  	num_irq = of_irq_count(np);  	/* Populate the resource table */ @@ -169,13 +132,12 @@ struct platform_device *of_device_alloc(struct device_node *np,  			rc = of_address_to_resource(np, i, res);  			WARN_ON(rc);  		} -		WARN_ON(of_irq_to_resource_table(np, res, num_irq) != num_irq); +		if (of_irq_to_resource_table(np, res, num_irq) != num_irq) +			pr_debug("not all legacy IRQ resources mapped for %s\n", +				 np->name);  	}  	dev->dev.of_node = of_node_get(np); -#if defined(CONFIG_MICROBLAZE) -	dev->dev.dma_mask = &dev->archdata.dma_mask; -#endif  	dev->dev.parent = parent;  	if (bus_id) @@ -188,6 +150,60 @@ struct platform_device *of_device_alloc(struct device_node *np,  EXPORT_SYMBOL(of_device_alloc);  /** + * of_dma_configure - Setup DMA configuration + * @dev:	Device to apply DMA configuration + * + * Try to get devices's DMA configuration from DT and update it + * accordingly. + * + * In case if platform code need to use own special DMA configuration,it + * can use Platform bus notifier and handle BUS_NOTIFY_ADD_DEVICE event + * to fix up DMA configuration. + */ +static void of_dma_configure(struct platform_device *pdev) +{ +	u64 dma_addr, paddr, size; +	int ret; +	struct device *dev = &pdev->dev; + +	/* +	 * Set default dma-mask to 32 bit. Drivers are expected to setup +	 * the correct supported dma_mask. +	 */ +	dev->coherent_dma_mask = DMA_BIT_MASK(32); + +	/* +	 * Set it to coherent_dma_mask by default if the architecture +	 * code has not set it. +	 */ +	if (!dev->dma_mask) +		dev->dma_mask = &dev->coherent_dma_mask; + +	/* +	 * if dma-coherent property exist, call arch hook to setup +	 * dma coherent operations. +	 */ +	if (of_dma_is_coherent(dev->of_node)) { +		set_arch_dma_coherent_ops(dev); +		dev_dbg(dev, "device is dma coherent\n"); +	} + +	/* +	 * if dma-ranges property doesn't exist - just return else +	 * setup the dma offset +	 */ +	ret = of_dma_get_range(dev->of_node, &dma_addr, &paddr, &size); +	if (ret < 0) { +		dev_dbg(dev, "no dma range information to setup\n"); +		return; +	} + +	/* DMA ranges found. Calculate and set dma_pfn_offset */ +	dev->dma_pfn_offset = PFN_DOWN(paddr - dma_addr); +	dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset); +} + +/**   * of_platform_device_create_pdata - Alloc, initialize and register an of_device   * @np: pointer to node to create device for   * @bus_id: name to assign device @@ -205,22 +221,18 @@ static struct platform_device *of_platform_device_create_pdata(  {  	struct platform_device *dev; -	if (!of_device_is_available(np)) +	if (!of_device_is_available(np) || +	    of_node_test_and_set_flag(np, OF_POPULATED))  		return NULL;  	dev = of_device_alloc(np, bus_id, parent);  	if (!dev) -		return NULL; +		goto err_clear_flag; -#if defined(CONFIG_MICROBLAZE) -	dev->archdata.dma_mask = 0xffffffffUL; -#endif -	dev->dev.coherent_dma_mask = DMA_BIT_MASK(32); +	of_dma_configure(dev);  	dev->dev.bus = &platform_bus_type;  	dev->dev.platform_data = platform_data; -	of_reserved_mem_device_init(&dev->dev); -  	/* We do not fill the DMA ops for platform devices by default.  	 * This is currently the responsibility of the platform code  	 * to do such, possibly using a device notifier @@ -228,11 +240,14 @@ static struct platform_device *of_platform_device_create_pdata(  	if (of_device_add(dev) != 0) {  		platform_device_put(dev); -		of_reserved_mem_device_release(&dev->dev); -		return NULL; +		goto err_clear_flag;  	}  	return dev; + +err_clear_flag: +	of_node_clear_flag(np, OF_POPULATED); +	return NULL;  }  /** @@ -264,14 +279,15 @@ static struct amba_device *of_amba_device_create(struct device_node *node,  	pr_debug("Creating amba device %s\n", node->full_name); -	if (!of_device_is_available(node)) +	if (!of_device_is_available(node) || +	    of_node_test_and_set_flag(node, OF_POPULATED))  		return NULL;  	dev = amba_device_alloc(NULL, 0, 0);  	if (!dev) {  		pr_err("%s(): amba_device_alloc() failed for %s\n",  		       __func__, node->full_name); -		return NULL; +		goto err_clear_flag;  	}  	/* setup generic device info */ @@ -284,9 +300,6 @@ static struct amba_device *of_amba_device_create(struct device_node *node,  	else  		of_device_make_bus_id(&dev->dev); -	/* setup amba-specific device info */ -	dev->dma_mask = ~0; -  	/* Allow the HW Peripheral ID to be overridden */  	prop = of_get_property(node, "arm,primecell-periphid", NULL);  	if (prop) @@ -314,6 +327,8 @@ static struct amba_device *of_amba_device_create(struct device_node *node,  err_free:  	amba_device_put(dev); +err_clear_flag: +	of_node_clear_flag(node, OF_POPULATED);  	return NULL;  }  #else /* CONFIG_ARM_AMBA */ @@ -490,4 +505,60 @@ int of_platform_populate(struct device_node *root,  	return rc;  }  EXPORT_SYMBOL_GPL(of_platform_populate); + +static int of_platform_device_destroy(struct device *dev, void *data) +{ +	bool *children_left = data; + +	/* Do not touch devices not populated from the device tree */ +	if (!dev->of_node || !of_node_check_flag(dev->of_node, OF_POPULATED)) { +		*children_left = true; +		return 0; +	} + +	/* Recurse, but don't touch this device if it has any children left */ +	if (of_platform_depopulate(dev) != 0) { +		*children_left = true; +		return 0; +	} + +	if (dev->bus == &platform_bus_type) +		platform_device_unregister(to_platform_device(dev)); +#ifdef CONFIG_ARM_AMBA +	else if (dev->bus == &amba_bustype) +		amba_device_unregister(to_amba_device(dev)); +#endif +	else { +		*children_left = true; +		return 0; +	} + +	of_node_clear_flag(dev->of_node, OF_POPULATED); + +	return 0; +} + +/** + * of_platform_depopulate() - Remove devices populated from device tree + * @parent: device which childred will be removed + * + * Complementary to of_platform_populate(), this function removes children + * of the given device (and, recurrently, their children) that have been + * created from their respective device tree nodes (and only those, + * leaving others - eg. manually created - unharmed). + * + * Returns 0 when all children devices have been removed or + * -EBUSY when some children remained. + */ +int of_platform_depopulate(struct device *parent) +{ +	bool children_left = false; + +	device_for_each_child(parent, &children_left, +			      of_platform_device_destroy); + +	return children_left ? -EBUSY : 0; +} +EXPORT_SYMBOL_GPL(of_platform_depopulate); +  #endif /* CONFIG_OF_ADDRESS */  | 
