162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2006 Benjamin Herrenschmidt, IBM Corp. 462306a36Sopenharmony_ci * <benh@kernel.crashing.org> 562306a36Sopenharmony_ci * and Arnd Bergmann, IBM Corp. 662306a36Sopenharmony_ci * Merged from powerpc/kernel/of_platform.c and 762306a36Sopenharmony_ci * sparc{,64}/kernel/of_device.c by Stephen Rothwell 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define pr_fmt(fmt) "OF: " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/errno.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/amba/bus.h> 1562306a36Sopenharmony_ci#include <linux/device.h> 1662306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/of_address.h> 1962306a36Sopenharmony_ci#include <linux/of_device.h> 2062306a36Sopenharmony_ci#include <linux/of_irq.h> 2162306a36Sopenharmony_ci#include <linux/of_platform.h> 2262306a36Sopenharmony_ci#include <linux/platform_device.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "of_private.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ciconst struct of_device_id of_default_bus_match_table[] = { 2762306a36Sopenharmony_ci { .compatible = "simple-bus", }, 2862306a36Sopenharmony_ci { .compatible = "simple-mfd", }, 2962306a36Sopenharmony_ci { .compatible = "isa", }, 3062306a36Sopenharmony_ci#ifdef CONFIG_ARM_AMBA 3162306a36Sopenharmony_ci { .compatible = "arm,amba-bus", }, 3262306a36Sopenharmony_ci#endif /* CONFIG_ARM_AMBA */ 3362306a36Sopenharmony_ci {} /* Empty terminated list */ 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/** 3762306a36Sopenharmony_ci * of_find_device_by_node - Find the platform_device associated with a node 3862306a36Sopenharmony_ci * @np: Pointer to device tree node 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * Takes a reference to the embedded struct device which needs to be dropped 4162306a36Sopenharmony_ci * after use. 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * Return: platform_device pointer, or NULL if not found 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_cistruct platform_device *of_find_device_by_node(struct device_node *np) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci struct device *dev; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci dev = bus_find_device_by_of_node(&platform_bus_type, np); 5062306a36Sopenharmony_ci return dev ? to_platform_device(dev) : NULL; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ciEXPORT_SYMBOL(of_find_device_by_node); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ciint of_device_add(struct platform_device *ofdev) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci BUG_ON(ofdev->dev.of_node == NULL); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci /* name and id have to be set so that the platform bus doesn't get 5962306a36Sopenharmony_ci * confused on matching */ 6062306a36Sopenharmony_ci ofdev->name = dev_name(&ofdev->dev); 6162306a36Sopenharmony_ci ofdev->id = PLATFORM_DEVID_NONE; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* 6462306a36Sopenharmony_ci * If this device has not binding numa node in devicetree, that is 6562306a36Sopenharmony_ci * of_node_to_nid returns NUMA_NO_NODE. device_add will assume that this 6662306a36Sopenharmony_ci * device is on the same node as the parent. 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ci set_dev_node(&ofdev->dev, of_node_to_nid(ofdev->dev.of_node)); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return device_add(&ofdev->dev); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ciint of_device_register(struct platform_device *pdev) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci device_initialize(&pdev->dev); 7662306a36Sopenharmony_ci return of_device_add(pdev); 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ciEXPORT_SYMBOL(of_device_register); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_civoid of_device_unregister(struct platform_device *ofdev) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci device_unregister(&ofdev->dev); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ciEXPORT_SYMBOL(of_device_unregister); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#ifdef CONFIG_OF_ADDRESS 8762306a36Sopenharmony_cistatic const struct of_device_id of_skipped_node_table[] = { 8862306a36Sopenharmony_ci { .compatible = "operating-points-v2", }, 8962306a36Sopenharmony_ci {} /* Empty terminated list */ 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* 9362306a36Sopenharmony_ci * The following routines scan a subtree and registers a device for 9462306a36Sopenharmony_ci * each applicable node. 9562306a36Sopenharmony_ci * 9662306a36Sopenharmony_ci * Note: sparc doesn't use these routines because it has a different 9762306a36Sopenharmony_ci * mechanism for creating devices from device tree nodes. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/** 10162306a36Sopenharmony_ci * of_device_make_bus_id - Use the device node data to assign a unique name 10262306a36Sopenharmony_ci * @dev: pointer to device structure that is linked to a device tree node 10362306a36Sopenharmony_ci * 10462306a36Sopenharmony_ci * This routine will first try using the translated bus address to 10562306a36Sopenharmony_ci * derive a unique name. If it cannot, then it will prepend names from 10662306a36Sopenharmony_ci * parent nodes until a unique name can be derived. 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_cistatic void of_device_make_bus_id(struct device *dev) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct device_node *node = dev->of_node; 11162306a36Sopenharmony_ci const __be32 *reg; 11262306a36Sopenharmony_ci u64 addr; 11362306a36Sopenharmony_ci u32 mask; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* Construct the name, using parent nodes if necessary to ensure uniqueness */ 11662306a36Sopenharmony_ci while (node->parent) { 11762306a36Sopenharmony_ci /* 11862306a36Sopenharmony_ci * If the address can be translated, then that is as much 11962306a36Sopenharmony_ci * uniqueness as we need. Make it the first component and return 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ci reg = of_get_property(node, "reg", NULL); 12262306a36Sopenharmony_ci if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) { 12362306a36Sopenharmony_ci if (!of_property_read_u32(node, "mask", &mask)) 12462306a36Sopenharmony_ci dev_set_name(dev, dev_name(dev) ? "%llx.%x.%pOFn:%s" : "%llx.%x.%pOFn", 12562306a36Sopenharmony_ci addr, ffs(mask) - 1, node, dev_name(dev)); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci else 12862306a36Sopenharmony_ci dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn", 12962306a36Sopenharmony_ci addr, node, dev_name(dev)); 13062306a36Sopenharmony_ci return; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* format arguments only used if dev_name() resolves to NULL */ 13462306a36Sopenharmony_ci dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s", 13562306a36Sopenharmony_ci kbasename(node->full_name), dev_name(dev)); 13662306a36Sopenharmony_ci node = node->parent; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/** 14162306a36Sopenharmony_ci * of_device_alloc - Allocate and initialize an of_device 14262306a36Sopenharmony_ci * @np: device node to assign to device 14362306a36Sopenharmony_ci * @bus_id: Name to assign to the device. May be null to use default name. 14462306a36Sopenharmony_ci * @parent: Parent device. 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_cistruct platform_device *of_device_alloc(struct device_node *np, 14762306a36Sopenharmony_ci const char *bus_id, 14862306a36Sopenharmony_ci struct device *parent) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct platform_device *dev; 15162306a36Sopenharmony_ci int rc, i, num_reg = 0; 15262306a36Sopenharmony_ci struct resource *res; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci dev = platform_device_alloc("", PLATFORM_DEVID_NONE); 15562306a36Sopenharmony_ci if (!dev) 15662306a36Sopenharmony_ci return NULL; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* count the io resources */ 15962306a36Sopenharmony_ci num_reg = of_address_count(np); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* Populate the resource table */ 16262306a36Sopenharmony_ci if (num_reg) { 16362306a36Sopenharmony_ci res = kcalloc(num_reg, sizeof(*res), GFP_KERNEL); 16462306a36Sopenharmony_ci if (!res) { 16562306a36Sopenharmony_ci platform_device_put(dev); 16662306a36Sopenharmony_ci return NULL; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci dev->num_resources = num_reg; 17062306a36Sopenharmony_ci dev->resource = res; 17162306a36Sopenharmony_ci for (i = 0; i < num_reg; i++, res++) { 17262306a36Sopenharmony_ci rc = of_address_to_resource(np, i, res); 17362306a36Sopenharmony_ci WARN_ON(rc); 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* setup generic device info */ 17862306a36Sopenharmony_ci device_set_node(&dev->dev, of_fwnode_handle(of_node_get(np))); 17962306a36Sopenharmony_ci dev->dev.parent = parent ? : &platform_bus; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (bus_id) 18262306a36Sopenharmony_ci dev_set_name(&dev->dev, "%s", bus_id); 18362306a36Sopenharmony_ci else 18462306a36Sopenharmony_ci of_device_make_bus_id(&dev->dev); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci return dev; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ciEXPORT_SYMBOL(of_device_alloc); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/** 19162306a36Sopenharmony_ci * of_platform_device_create_pdata - Alloc, initialize and register an of_device 19262306a36Sopenharmony_ci * @np: pointer to node to create device for 19362306a36Sopenharmony_ci * @bus_id: name to assign device 19462306a36Sopenharmony_ci * @platform_data: pointer to populate platform_data pointer with 19562306a36Sopenharmony_ci * @parent: Linux device model parent device. 19662306a36Sopenharmony_ci * 19762306a36Sopenharmony_ci * Return: Pointer to created platform device, or NULL if a device was not 19862306a36Sopenharmony_ci * registered. Unavailable devices will not get registered. 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_cistatic struct platform_device *of_platform_device_create_pdata( 20162306a36Sopenharmony_ci struct device_node *np, 20262306a36Sopenharmony_ci const char *bus_id, 20362306a36Sopenharmony_ci void *platform_data, 20462306a36Sopenharmony_ci struct device *parent) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct platform_device *dev; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (!of_device_is_available(np) || 20962306a36Sopenharmony_ci of_node_test_and_set_flag(np, OF_POPULATED)) 21062306a36Sopenharmony_ci return NULL; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci dev = of_device_alloc(np, bus_id, parent); 21362306a36Sopenharmony_ci if (!dev) 21462306a36Sopenharmony_ci goto err_clear_flag; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci dev->dev.coherent_dma_mask = DMA_BIT_MASK(32); 21762306a36Sopenharmony_ci if (!dev->dev.dma_mask) 21862306a36Sopenharmony_ci dev->dev.dma_mask = &dev->dev.coherent_dma_mask; 21962306a36Sopenharmony_ci dev->dev.bus = &platform_bus_type; 22062306a36Sopenharmony_ci dev->dev.platform_data = platform_data; 22162306a36Sopenharmony_ci of_msi_configure(&dev->dev, dev->dev.of_node); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (of_device_add(dev) != 0) { 22462306a36Sopenharmony_ci platform_device_put(dev); 22562306a36Sopenharmony_ci goto err_clear_flag; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci return dev; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cierr_clear_flag: 23162306a36Sopenharmony_ci of_node_clear_flag(np, OF_POPULATED); 23262306a36Sopenharmony_ci return NULL; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci/** 23662306a36Sopenharmony_ci * of_platform_device_create - Alloc, initialize and register an of_device 23762306a36Sopenharmony_ci * @np: pointer to node to create device for 23862306a36Sopenharmony_ci * @bus_id: name to assign device 23962306a36Sopenharmony_ci * @parent: Linux device model parent device. 24062306a36Sopenharmony_ci * 24162306a36Sopenharmony_ci * Return: Pointer to created platform device, or NULL if a device was not 24262306a36Sopenharmony_ci * registered. Unavailable devices will not get registered. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_cistruct platform_device *of_platform_device_create(struct device_node *np, 24562306a36Sopenharmony_ci const char *bus_id, 24662306a36Sopenharmony_ci struct device *parent) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci return of_platform_device_create_pdata(np, bus_id, NULL, parent); 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ciEXPORT_SYMBOL(of_platform_device_create); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci#ifdef CONFIG_ARM_AMBA 25362306a36Sopenharmony_cistatic struct amba_device *of_amba_device_create(struct device_node *node, 25462306a36Sopenharmony_ci const char *bus_id, 25562306a36Sopenharmony_ci void *platform_data, 25662306a36Sopenharmony_ci struct device *parent) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct amba_device *dev; 25962306a36Sopenharmony_ci int ret; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci pr_debug("Creating amba device %pOF\n", node); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (!of_device_is_available(node) || 26462306a36Sopenharmony_ci of_node_test_and_set_flag(node, OF_POPULATED)) 26562306a36Sopenharmony_ci return NULL; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci dev = amba_device_alloc(NULL, 0, 0); 26862306a36Sopenharmony_ci if (!dev) 26962306a36Sopenharmony_ci goto err_clear_flag; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* AMBA devices only support a single DMA mask */ 27262306a36Sopenharmony_ci dev->dev.coherent_dma_mask = DMA_BIT_MASK(32); 27362306a36Sopenharmony_ci dev->dev.dma_mask = &dev->dev.coherent_dma_mask; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* setup generic device info */ 27662306a36Sopenharmony_ci device_set_node(&dev->dev, of_fwnode_handle(of_node_get(node))); 27762306a36Sopenharmony_ci dev->dev.parent = parent ? : &platform_bus; 27862306a36Sopenharmony_ci dev->dev.platform_data = platform_data; 27962306a36Sopenharmony_ci if (bus_id) 28062306a36Sopenharmony_ci dev_set_name(&dev->dev, "%s", bus_id); 28162306a36Sopenharmony_ci else 28262306a36Sopenharmony_ci of_device_make_bus_id(&dev->dev); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* Allow the HW Peripheral ID to be overridden */ 28562306a36Sopenharmony_ci of_property_read_u32(node, "arm,primecell-periphid", &dev->periphid); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci ret = of_address_to_resource(node, 0, &dev->res); 28862306a36Sopenharmony_ci if (ret) { 28962306a36Sopenharmony_ci pr_err("amba: of_address_to_resource() failed (%d) for %pOF\n", 29062306a36Sopenharmony_ci ret, node); 29162306a36Sopenharmony_ci goto err_free; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci ret = amba_device_add(dev, &iomem_resource); 29562306a36Sopenharmony_ci if (ret) { 29662306a36Sopenharmony_ci pr_err("amba_device_add() failed (%d) for %pOF\n", 29762306a36Sopenharmony_ci ret, node); 29862306a36Sopenharmony_ci goto err_free; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci return dev; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cierr_free: 30462306a36Sopenharmony_ci amba_device_put(dev); 30562306a36Sopenharmony_cierr_clear_flag: 30662306a36Sopenharmony_ci of_node_clear_flag(node, OF_POPULATED); 30762306a36Sopenharmony_ci return NULL; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci#else /* CONFIG_ARM_AMBA */ 31062306a36Sopenharmony_cistatic struct amba_device *of_amba_device_create(struct device_node *node, 31162306a36Sopenharmony_ci const char *bus_id, 31262306a36Sopenharmony_ci void *platform_data, 31362306a36Sopenharmony_ci struct device *parent) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci return NULL; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci#endif /* CONFIG_ARM_AMBA */ 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci/* 32062306a36Sopenharmony_ci * of_dev_lookup() - Given a device node, lookup the preferred Linux name 32162306a36Sopenharmony_ci */ 32262306a36Sopenharmony_cistatic const struct of_dev_auxdata *of_dev_lookup(const struct of_dev_auxdata *lookup, 32362306a36Sopenharmony_ci struct device_node *np) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci const struct of_dev_auxdata *auxdata; 32662306a36Sopenharmony_ci struct resource res; 32762306a36Sopenharmony_ci int compatible = 0; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (!lookup) 33062306a36Sopenharmony_ci return NULL; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci auxdata = lookup; 33362306a36Sopenharmony_ci for (; auxdata->compatible; auxdata++) { 33462306a36Sopenharmony_ci if (!of_device_is_compatible(np, auxdata->compatible)) 33562306a36Sopenharmony_ci continue; 33662306a36Sopenharmony_ci compatible++; 33762306a36Sopenharmony_ci if (!of_address_to_resource(np, 0, &res)) 33862306a36Sopenharmony_ci if (res.start != auxdata->phys_addr) 33962306a36Sopenharmony_ci continue; 34062306a36Sopenharmony_ci pr_debug("%pOF: devname=%s\n", np, auxdata->name); 34162306a36Sopenharmony_ci return auxdata; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (!compatible) 34562306a36Sopenharmony_ci return NULL; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci /* Try compatible match if no phys_addr and name are specified */ 34862306a36Sopenharmony_ci auxdata = lookup; 34962306a36Sopenharmony_ci for (; auxdata->compatible; auxdata++) { 35062306a36Sopenharmony_ci if (!of_device_is_compatible(np, auxdata->compatible)) 35162306a36Sopenharmony_ci continue; 35262306a36Sopenharmony_ci if (!auxdata->phys_addr && !auxdata->name) { 35362306a36Sopenharmony_ci pr_debug("%pOF: compatible match\n", np); 35462306a36Sopenharmony_ci return auxdata; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci return NULL; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci/** 36262306a36Sopenharmony_ci * of_platform_bus_create() - Create a device for a node and its children. 36362306a36Sopenharmony_ci * @bus: device node of the bus to instantiate 36462306a36Sopenharmony_ci * @matches: match table for bus nodes 36562306a36Sopenharmony_ci * @lookup: auxdata table for matching id and platform_data with device nodes 36662306a36Sopenharmony_ci * @parent: parent for new device, or NULL for top level. 36762306a36Sopenharmony_ci * @strict: require compatible property 36862306a36Sopenharmony_ci * 36962306a36Sopenharmony_ci * Creates a platform_device for the provided device_node, and optionally 37062306a36Sopenharmony_ci * recursively create devices for all the child nodes. 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_cistatic int of_platform_bus_create(struct device_node *bus, 37362306a36Sopenharmony_ci const struct of_device_id *matches, 37462306a36Sopenharmony_ci const struct of_dev_auxdata *lookup, 37562306a36Sopenharmony_ci struct device *parent, bool strict) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci const struct of_dev_auxdata *auxdata; 37862306a36Sopenharmony_ci struct device_node *child; 37962306a36Sopenharmony_ci struct platform_device *dev; 38062306a36Sopenharmony_ci const char *bus_id = NULL; 38162306a36Sopenharmony_ci void *platform_data = NULL; 38262306a36Sopenharmony_ci int rc = 0; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* Make sure it has a compatible property */ 38562306a36Sopenharmony_ci if (strict && (!of_get_property(bus, "compatible", NULL))) { 38662306a36Sopenharmony_ci pr_debug("%s() - skipping %pOF, no compatible prop\n", 38762306a36Sopenharmony_ci __func__, bus); 38862306a36Sopenharmony_ci return 0; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* Skip nodes for which we don't want to create devices */ 39262306a36Sopenharmony_ci if (unlikely(of_match_node(of_skipped_node_table, bus))) { 39362306a36Sopenharmony_ci pr_debug("%s() - skipping %pOF node\n", __func__, bus); 39462306a36Sopenharmony_ci return 0; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (of_node_check_flag(bus, OF_POPULATED_BUS)) { 39862306a36Sopenharmony_ci pr_debug("%s() - skipping %pOF, already populated\n", 39962306a36Sopenharmony_ci __func__, bus); 40062306a36Sopenharmony_ci return 0; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci auxdata = of_dev_lookup(lookup, bus); 40462306a36Sopenharmony_ci if (auxdata) { 40562306a36Sopenharmony_ci bus_id = auxdata->name; 40662306a36Sopenharmony_ci platform_data = auxdata->platform_data; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (of_device_is_compatible(bus, "arm,primecell")) { 41062306a36Sopenharmony_ci /* 41162306a36Sopenharmony_ci * Don't return an error here to keep compatibility with older 41262306a36Sopenharmony_ci * device tree files. 41362306a36Sopenharmony_ci */ 41462306a36Sopenharmony_ci of_amba_device_create(bus, bus_id, platform_data, parent); 41562306a36Sopenharmony_ci return 0; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent); 41962306a36Sopenharmony_ci if (!dev || !of_match_node(matches, bus)) 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci for_each_child_of_node(bus, child) { 42362306a36Sopenharmony_ci pr_debug(" create child: %pOF\n", child); 42462306a36Sopenharmony_ci rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict); 42562306a36Sopenharmony_ci if (rc) { 42662306a36Sopenharmony_ci of_node_put(child); 42762306a36Sopenharmony_ci break; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci of_node_set_flag(bus, OF_POPULATED_BUS); 43162306a36Sopenharmony_ci return rc; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci/** 43562306a36Sopenharmony_ci * of_platform_bus_probe() - Probe the device-tree for platform buses 43662306a36Sopenharmony_ci * @root: parent of the first level to probe or NULL for the root of the tree 43762306a36Sopenharmony_ci * @matches: match table for bus nodes 43862306a36Sopenharmony_ci * @parent: parent to hook devices from, NULL for toplevel 43962306a36Sopenharmony_ci * 44062306a36Sopenharmony_ci * Note that children of the provided root are not instantiated as devices 44162306a36Sopenharmony_ci * unless the specified root itself matches the bus list and is not NULL. 44262306a36Sopenharmony_ci */ 44362306a36Sopenharmony_ciint of_platform_bus_probe(struct device_node *root, 44462306a36Sopenharmony_ci const struct of_device_id *matches, 44562306a36Sopenharmony_ci struct device *parent) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci struct device_node *child; 44862306a36Sopenharmony_ci int rc = 0; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci root = root ? of_node_get(root) : of_find_node_by_path("/"); 45162306a36Sopenharmony_ci if (!root) 45262306a36Sopenharmony_ci return -EINVAL; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci pr_debug("%s()\n", __func__); 45562306a36Sopenharmony_ci pr_debug(" starting at: %pOF\n", root); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* Do a self check of bus type, if there's a match, create children */ 45862306a36Sopenharmony_ci if (of_match_node(matches, root)) { 45962306a36Sopenharmony_ci rc = of_platform_bus_create(root, matches, NULL, parent, false); 46062306a36Sopenharmony_ci } else for_each_child_of_node(root, child) { 46162306a36Sopenharmony_ci if (!of_match_node(matches, child)) 46262306a36Sopenharmony_ci continue; 46362306a36Sopenharmony_ci rc = of_platform_bus_create(child, matches, NULL, parent, false); 46462306a36Sopenharmony_ci if (rc) { 46562306a36Sopenharmony_ci of_node_put(child); 46662306a36Sopenharmony_ci break; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci of_node_put(root); 47162306a36Sopenharmony_ci return rc; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ciEXPORT_SYMBOL(of_platform_bus_probe); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci/** 47662306a36Sopenharmony_ci * of_platform_populate() - Populate platform_devices from device tree data 47762306a36Sopenharmony_ci * @root: parent of the first level to probe or NULL for the root of the tree 47862306a36Sopenharmony_ci * @matches: match table, NULL to use the default 47962306a36Sopenharmony_ci * @lookup: auxdata table for matching id and platform_data with device nodes 48062306a36Sopenharmony_ci * @parent: parent to hook devices from, NULL for toplevel 48162306a36Sopenharmony_ci * 48262306a36Sopenharmony_ci * Similar to of_platform_bus_probe(), this function walks the device tree 48362306a36Sopenharmony_ci * and creates devices from nodes. It differs in that it follows the modern 48462306a36Sopenharmony_ci * convention of requiring all device nodes to have a 'compatible' property, 48562306a36Sopenharmony_ci * and it is suitable for creating devices which are children of the root 48662306a36Sopenharmony_ci * node (of_platform_bus_probe will only create children of the root which 48762306a36Sopenharmony_ci * are selected by the @matches argument). 48862306a36Sopenharmony_ci * 48962306a36Sopenharmony_ci * New board support should be using this function instead of 49062306a36Sopenharmony_ci * of_platform_bus_probe(). 49162306a36Sopenharmony_ci * 49262306a36Sopenharmony_ci * Return: 0 on success, < 0 on failure. 49362306a36Sopenharmony_ci */ 49462306a36Sopenharmony_ciint of_platform_populate(struct device_node *root, 49562306a36Sopenharmony_ci const struct of_device_id *matches, 49662306a36Sopenharmony_ci const struct of_dev_auxdata *lookup, 49762306a36Sopenharmony_ci struct device *parent) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci struct device_node *child; 50062306a36Sopenharmony_ci int rc = 0; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci root = root ? of_node_get(root) : of_find_node_by_path("/"); 50362306a36Sopenharmony_ci if (!root) 50462306a36Sopenharmony_ci return -EINVAL; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci pr_debug("%s()\n", __func__); 50762306a36Sopenharmony_ci pr_debug(" starting at: %pOF\n", root); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci device_links_supplier_sync_state_pause(); 51062306a36Sopenharmony_ci for_each_child_of_node(root, child) { 51162306a36Sopenharmony_ci rc = of_platform_bus_create(child, matches, lookup, parent, true); 51262306a36Sopenharmony_ci if (rc) { 51362306a36Sopenharmony_ci of_node_put(child); 51462306a36Sopenharmony_ci break; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci device_links_supplier_sync_state_resume(); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci of_node_set_flag(root, OF_POPULATED_BUS); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci of_node_put(root); 52262306a36Sopenharmony_ci return rc; 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_platform_populate); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ciint of_platform_default_populate(struct device_node *root, 52762306a36Sopenharmony_ci const struct of_dev_auxdata *lookup, 52862306a36Sopenharmony_ci struct device *parent) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci return of_platform_populate(root, of_default_bus_match_table, lookup, 53162306a36Sopenharmony_ci parent); 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_platform_default_populate); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic const struct of_device_id reserved_mem_matches[] = { 53662306a36Sopenharmony_ci { .compatible = "phram" }, 53762306a36Sopenharmony_ci { .compatible = "qcom,rmtfs-mem" }, 53862306a36Sopenharmony_ci { .compatible = "qcom,cmd-db" }, 53962306a36Sopenharmony_ci { .compatible = "qcom,smem" }, 54062306a36Sopenharmony_ci { .compatible = "ramoops" }, 54162306a36Sopenharmony_ci { .compatible = "nvmem-rmem" }, 54262306a36Sopenharmony_ci { .compatible = "google,open-dice" }, 54362306a36Sopenharmony_ci {} 54462306a36Sopenharmony_ci}; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistatic int __init of_platform_default_populate_init(void) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci struct device_node *node; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci device_links_supplier_sync_state_pause(); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (!of_have_populated_dt()) 55362306a36Sopenharmony_ci return -ENODEV; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PPC)) { 55662306a36Sopenharmony_ci struct device_node *boot_display = NULL; 55762306a36Sopenharmony_ci struct platform_device *dev; 55862306a36Sopenharmony_ci int display_number = 0; 55962306a36Sopenharmony_ci int ret; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* Check if we have a MacOS display without a node spec */ 56262306a36Sopenharmony_ci if (of_property_present(of_chosen, "linux,bootx-noscreen")) { 56362306a36Sopenharmony_ci /* 56462306a36Sopenharmony_ci * The old code tried to work out which node was the MacOS 56562306a36Sopenharmony_ci * display based on the address. I'm dropping that since the 56662306a36Sopenharmony_ci * lack of a node spec only happens with old BootX versions 56762306a36Sopenharmony_ci * (users can update) and with this code, they'll still get 56862306a36Sopenharmony_ci * a display (just not the palette hacks). 56962306a36Sopenharmony_ci */ 57062306a36Sopenharmony_ci dev = platform_device_alloc("bootx-noscreen", 0); 57162306a36Sopenharmony_ci if (WARN_ON(!dev)) 57262306a36Sopenharmony_ci return -ENOMEM; 57362306a36Sopenharmony_ci ret = platform_device_add(dev); 57462306a36Sopenharmony_ci if (WARN_ON(ret)) { 57562306a36Sopenharmony_ci platform_device_put(dev); 57662306a36Sopenharmony_ci return ret; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* 58162306a36Sopenharmony_ci * For OF framebuffers, first create the device for the boot display, 58262306a36Sopenharmony_ci * then for the other framebuffers. Only fail for the boot display; 58362306a36Sopenharmony_ci * ignore errors for the rest. 58462306a36Sopenharmony_ci */ 58562306a36Sopenharmony_ci for_each_node_by_type(node, "display") { 58662306a36Sopenharmony_ci if (!of_get_property(node, "linux,opened", NULL) || 58762306a36Sopenharmony_ci !of_get_property(node, "linux,boot-display", NULL)) 58862306a36Sopenharmony_ci continue; 58962306a36Sopenharmony_ci dev = of_platform_device_create(node, "of-display", NULL); 59062306a36Sopenharmony_ci of_node_put(node); 59162306a36Sopenharmony_ci if (WARN_ON(!dev)) 59262306a36Sopenharmony_ci return -ENOMEM; 59362306a36Sopenharmony_ci boot_display = node; 59462306a36Sopenharmony_ci display_number++; 59562306a36Sopenharmony_ci break; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci for_each_node_by_type(node, "display") { 59862306a36Sopenharmony_ci char buf[14]; 59962306a36Sopenharmony_ci const char *of_display_format = "of-display.%d"; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci if (!of_get_property(node, "linux,opened", NULL) || node == boot_display) 60262306a36Sopenharmony_ci continue; 60362306a36Sopenharmony_ci ret = snprintf(buf, sizeof(buf), of_display_format, display_number++); 60462306a36Sopenharmony_ci if (ret < sizeof(buf)) 60562306a36Sopenharmony_ci of_platform_device_create(node, buf, NULL); 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci } else { 60962306a36Sopenharmony_ci /* 61062306a36Sopenharmony_ci * Handle certain compatibles explicitly, since we don't want to create 61162306a36Sopenharmony_ci * platform_devices for every node in /reserved-memory with a 61262306a36Sopenharmony_ci * "compatible", 61362306a36Sopenharmony_ci */ 61462306a36Sopenharmony_ci for_each_matching_node(node, reserved_mem_matches) 61562306a36Sopenharmony_ci of_platform_device_create(node, NULL, NULL); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci node = of_find_node_by_path("/firmware"); 61862306a36Sopenharmony_ci if (node) { 61962306a36Sopenharmony_ci of_platform_populate(node, NULL, NULL, NULL); 62062306a36Sopenharmony_ci of_node_put(node); 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci node = of_get_compatible_child(of_chosen, "simple-framebuffer"); 62462306a36Sopenharmony_ci of_platform_device_create(node, NULL, NULL); 62562306a36Sopenharmony_ci of_node_put(node); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci /* Populate everything else. */ 62862306a36Sopenharmony_ci of_platform_default_populate(NULL, NULL, NULL); 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci return 0; 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ciarch_initcall_sync(of_platform_default_populate_init); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic int __init of_platform_sync_state_init(void) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci device_links_supplier_sync_state_resume(); 63862306a36Sopenharmony_ci return 0; 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_cilate_initcall_sync(of_platform_sync_state_init); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ciint of_platform_device_destroy(struct device *dev, void *data) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci /* Do not touch devices not populated from the device tree */ 64562306a36Sopenharmony_ci if (!dev->of_node || !of_node_check_flag(dev->of_node, OF_POPULATED)) 64662306a36Sopenharmony_ci return 0; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci /* Recurse for any nodes that were treated as busses */ 64962306a36Sopenharmony_ci if (of_node_check_flag(dev->of_node, OF_POPULATED_BUS)) 65062306a36Sopenharmony_ci device_for_each_child(dev, NULL, of_platform_device_destroy); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci of_node_clear_flag(dev->of_node, OF_POPULATED); 65362306a36Sopenharmony_ci of_node_clear_flag(dev->of_node, OF_POPULATED_BUS); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (dev->bus == &platform_bus_type) 65662306a36Sopenharmony_ci platform_device_unregister(to_platform_device(dev)); 65762306a36Sopenharmony_ci#ifdef CONFIG_ARM_AMBA 65862306a36Sopenharmony_ci else if (dev->bus == &amba_bustype) 65962306a36Sopenharmony_ci amba_device_unregister(to_amba_device(dev)); 66062306a36Sopenharmony_ci#endif 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci return 0; 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_platform_device_destroy); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci/** 66762306a36Sopenharmony_ci * of_platform_depopulate() - Remove devices populated from device tree 66862306a36Sopenharmony_ci * @parent: device which children will be removed 66962306a36Sopenharmony_ci * 67062306a36Sopenharmony_ci * Complementary to of_platform_populate(), this function removes children 67162306a36Sopenharmony_ci * of the given device (and, recurrently, their children) that have been 67262306a36Sopenharmony_ci * created from their respective device tree nodes (and only those, 67362306a36Sopenharmony_ci * leaving others - eg. manually created - unharmed). 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_civoid of_platform_depopulate(struct device *parent) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci if (parent->of_node && of_node_check_flag(parent->of_node, OF_POPULATED_BUS)) { 67862306a36Sopenharmony_ci device_for_each_child_reverse(parent, NULL, of_platform_device_destroy); 67962306a36Sopenharmony_ci of_node_clear_flag(parent->of_node, OF_POPULATED_BUS); 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci} 68262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_platform_depopulate); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistatic void devm_of_platform_populate_release(struct device *dev, void *res) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci of_platform_depopulate(*(struct device **)res); 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci/** 69062306a36Sopenharmony_ci * devm_of_platform_populate() - Populate platform_devices from device tree data 69162306a36Sopenharmony_ci * @dev: device that requested to populate from device tree data 69262306a36Sopenharmony_ci * 69362306a36Sopenharmony_ci * Similar to of_platform_populate(), but will automatically call 69462306a36Sopenharmony_ci * of_platform_depopulate() when the device is unbound from the bus. 69562306a36Sopenharmony_ci * 69662306a36Sopenharmony_ci * Return: 0 on success, < 0 on failure. 69762306a36Sopenharmony_ci */ 69862306a36Sopenharmony_ciint devm_of_platform_populate(struct device *dev) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci struct device **ptr; 70162306a36Sopenharmony_ci int ret; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci if (!dev) 70462306a36Sopenharmony_ci return -EINVAL; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci ptr = devres_alloc(devm_of_platform_populate_release, 70762306a36Sopenharmony_ci sizeof(*ptr), GFP_KERNEL); 70862306a36Sopenharmony_ci if (!ptr) 70962306a36Sopenharmony_ci return -ENOMEM; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci ret = of_platform_populate(dev->of_node, NULL, NULL, dev); 71262306a36Sopenharmony_ci if (ret) { 71362306a36Sopenharmony_ci devres_free(ptr); 71462306a36Sopenharmony_ci } else { 71562306a36Sopenharmony_ci *ptr = dev; 71662306a36Sopenharmony_ci devres_add(dev, ptr); 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci return ret; 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_of_platform_populate); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_cistatic int devm_of_platform_match(struct device *dev, void *res, void *data) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci struct device **ptr = res; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if (!ptr) { 72862306a36Sopenharmony_ci WARN_ON(!ptr); 72962306a36Sopenharmony_ci return 0; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci return *ptr == data; 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci/** 73662306a36Sopenharmony_ci * devm_of_platform_depopulate() - Remove devices populated from device tree 73762306a36Sopenharmony_ci * @dev: device that requested to depopulate from device tree data 73862306a36Sopenharmony_ci * 73962306a36Sopenharmony_ci * Complementary to devm_of_platform_populate(), this function removes children 74062306a36Sopenharmony_ci * of the given device (and, recurrently, their children) that have been 74162306a36Sopenharmony_ci * created from their respective device tree nodes (and only those, 74262306a36Sopenharmony_ci * leaving others - eg. manually created - unharmed). 74362306a36Sopenharmony_ci */ 74462306a36Sopenharmony_civoid devm_of_platform_depopulate(struct device *dev) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci int ret; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci ret = devres_release(dev, devm_of_platform_populate_release, 74962306a36Sopenharmony_ci devm_of_platform_match, dev); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci WARN_ON(ret); 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_of_platform_depopulate); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci#ifdef CONFIG_OF_DYNAMIC 75662306a36Sopenharmony_cistatic int of_platform_notify(struct notifier_block *nb, 75762306a36Sopenharmony_ci unsigned long action, void *arg) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci struct of_reconfig_data *rd = arg; 76062306a36Sopenharmony_ci struct platform_device *pdev_parent, *pdev; 76162306a36Sopenharmony_ci bool children_left; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci switch (of_reconfig_get_state_change(action, rd)) { 76462306a36Sopenharmony_ci case OF_RECONFIG_CHANGE_ADD: 76562306a36Sopenharmony_ci /* verify that the parent is a bus */ 76662306a36Sopenharmony_ci if (!of_node_check_flag(rd->dn->parent, OF_POPULATED_BUS)) 76762306a36Sopenharmony_ci return NOTIFY_OK; /* not for us */ 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* already populated? (driver using of_populate manually) */ 77062306a36Sopenharmony_ci if (of_node_check_flag(rd->dn, OF_POPULATED)) 77162306a36Sopenharmony_ci return NOTIFY_OK; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci /* 77462306a36Sopenharmony_ci * Clear the flag before adding the device so that fw_devlink 77562306a36Sopenharmony_ci * doesn't skip adding consumers to this device. 77662306a36Sopenharmony_ci */ 77762306a36Sopenharmony_ci rd->dn->fwnode.flags &= ~FWNODE_FLAG_NOT_DEVICE; 77862306a36Sopenharmony_ci /* pdev_parent may be NULL when no bus platform device */ 77962306a36Sopenharmony_ci pdev_parent = of_find_device_by_node(rd->dn->parent); 78062306a36Sopenharmony_ci pdev = of_platform_device_create(rd->dn, NULL, 78162306a36Sopenharmony_ci pdev_parent ? &pdev_parent->dev : NULL); 78262306a36Sopenharmony_ci platform_device_put(pdev_parent); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci if (pdev == NULL) { 78562306a36Sopenharmony_ci pr_err("%s: failed to create for '%pOF'\n", 78662306a36Sopenharmony_ci __func__, rd->dn); 78762306a36Sopenharmony_ci /* of_platform_device_create tosses the error code */ 78862306a36Sopenharmony_ci return notifier_from_errno(-EINVAL); 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci break; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci case OF_RECONFIG_CHANGE_REMOVE: 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci /* already depopulated? */ 79562306a36Sopenharmony_ci if (!of_node_check_flag(rd->dn, OF_POPULATED)) 79662306a36Sopenharmony_ci return NOTIFY_OK; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci /* find our device by node */ 79962306a36Sopenharmony_ci pdev = of_find_device_by_node(rd->dn); 80062306a36Sopenharmony_ci if (pdev == NULL) 80162306a36Sopenharmony_ci return NOTIFY_OK; /* no? not meant for us */ 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci /* unregister takes one ref away */ 80462306a36Sopenharmony_ci of_platform_device_destroy(&pdev->dev, &children_left); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci /* and put the reference of the find */ 80762306a36Sopenharmony_ci platform_device_put(pdev); 80862306a36Sopenharmony_ci break; 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci return NOTIFY_OK; 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cistatic struct notifier_block platform_of_notifier = { 81562306a36Sopenharmony_ci .notifier_call = of_platform_notify, 81662306a36Sopenharmony_ci}; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_civoid of_platform_register_reconfig_notifier(void) 81962306a36Sopenharmony_ci{ 82062306a36Sopenharmony_ci WARN_ON(of_reconfig_notifier_register(&platform_of_notifier)); 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci#endif /* CONFIG_OF_DYNAMIC */ 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci#endif /* CONFIG_OF_ADDRESS */ 825