2017-12-27 18:55:14 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2007-05-02 16:38:57 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2006 Benjamin Herrenschmidt, IBM Corp.
|
|
|
|
* <benh@kernel.crashing.org>
|
|
|
|
* and Arnd Bergmann, IBM Corp.
|
|
|
|
* Merged from powerpc/kernel/of_platform.c and
|
|
|
|
* sparc{,64}/kernel/of_device.c by Stephen Rothwell
|
|
|
|
*/
|
2016-06-15 13:32:18 +00:00
|
|
|
|
|
|
|
#define pr_fmt(fmt) "OF: " fmt
|
|
|
|
|
2007-05-02 16:38:57 +00:00
|
|
|
#include <linux/errno.h>
|
2007-10-18 04:17:42 +00:00
|
|
|
#include <linux/module.h>
|
2011-06-21 16:59:34 +00:00
|
|
|
#include <linux/amba/bus.h>
|
2007-05-02 16:38:57 +00:00
|
|
|
#include <linux/device.h>
|
2010-06-08 13:48:13 +00:00
|
|
|
#include <linux/dma-mapping.h>
|
2010-06-08 13:48:20 +00:00
|
|
|
#include <linux/slab.h>
|
2010-06-08 13:48:26 +00:00
|
|
|
#include <linux/of_address.h>
|
2007-05-02 16:38:57 +00:00
|
|
|
#include <linux/of_device.h>
|
2010-06-08 13:48:26 +00:00
|
|
|
#include <linux/of_irq.h>
|
2007-05-02 16:38:57 +00:00
|
|
|
#include <linux/of_platform.h>
|
2010-06-08 13:48:21 +00:00
|
|
|
#include <linux/platform_device.h>
|
of/platform: Disable sysfb if a simple-framebuffer node is found
JIRA: https://issues.redhat.com/browse/RHEL-53899
Upstream Status: v6.8-rc1
commit 3310288f61357b9b54139c93fc7665d60c0288bf
Author: Javier Martinez Canillas <javierm@redhat.com>
AuthorDate: Mon Nov 13 09:51:41 2023 +0100
Commit: Rob Herring <robh@kernel.org>
CommitDate: Thu Dec 7 11:30:06 2023 -0600
Some DT platforms use EFI to boot and in this case the EFI Boot Services
may register a EFI_GRAPHICS_OUTPUT_PROTOCOL handle, that will later be
queried by the Linux EFI stub to fill the global struct screen_info data.
The data is used by the Generic System Framebuffers (sysfb) framework to
add a platform device with platform data about the system framebuffer.
But if there is a "simple-framebuffer" node in the DT, the OF core will
also do the same and add another device for the system framebuffer.
This could lead for example, to two platform devices ("simple-framebuffer"
and "efi-framebuffer") to be added and matched with their corresponding
drivers. So both efifb and simpledrm will be probed, leading to following:
[ 0.055752] efifb: framebuffer at 0xbd58dc000, using 16000k, total 16000k
[ 0.055755] efifb: mode is 2560x1600x32, linelength=10240, pages=1
[ 0.055758] efifb: scrolling: redraw
[ 0.055759] efifb: Truecolor: size=2:10:10:10, shift=30:20:10:0
...
[ 3.295896] simple-framebuffer bd58dc000.framebuffer: [drm] *ERROR*
could not acquire memory range [??? 0xffff79f30a29ee40-0x2a5000001a7
flags 0x0]: -16
[ 3.298018] simple-framebuffer: probe of bd58dc000.framebuffer
failed with error -16
To prevent the issue, make the OF core to disable sysfb if there is a node
with a "simple-framebuffer" compatible. That way only this device will be
registered and sysfb would not attempt to register another one using the
screen_info data even if this has been filled.
This seems the correct thing to do in this case because:
a) On a DT platform, the DTB is the single source of truth since is what
describes the hardware topology. Even if EFI Boot Services are used to
boot the machine.
b) The of_platform_default_populate_init() function is called in the
arch_initcall_sync() initcall level while the sysfb_init() function
is called later in the subsys_initcall() initcall level.
Reported-by: Andrew Worsley <amworsley@gmail.com>
Closes: https://lore.kernel.org/all/20231111042926.52990-2-amworsley@gmail.com
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
Reviewed-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://lore.kernel.org/r/20231113085305.1823455-1-javierm@redhat.com
Signed-off-by: Rob Herring <robh@kernel.org>
Signed-off-by: Jocelyn Falempe <jfalempe@redhat.com>
2024-11-07 10:31:09 +00:00
|
|
|
#include <linux/sysfb.h>
|
2010-06-08 13:48:21 +00:00
|
|
|
|
2024-04-23 18:21:41 +00:00
|
|
|
#include "of_private.h"
|
|
|
|
|
2011-06-21 16:59:34 +00:00
|
|
|
const struct of_device_id of_default_bus_match_table[] = {
|
|
|
|
{ .compatible = "simple-bus", },
|
2015-03-03 08:52:20 +00:00
|
|
|
{ .compatible = "simple-mfd", },
|
2016-09-19 21:21:24 +00:00
|
|
|
{ .compatible = "isa", },
|
2011-06-21 16:59:34 +00:00
|
|
|
#ifdef CONFIG_ARM_AMBA
|
|
|
|
{ .compatible = "arm,amba-bus", },
|
|
|
|
#endif /* CONFIG_ARM_AMBA */
|
|
|
|
{} /* Empty terminated list */
|
|
|
|
};
|
|
|
|
|
2018-04-17 06:27:34 +00:00
|
|
|
static const struct of_device_id of_skipped_node_table[] = {
|
|
|
|
{ .compatible = "operating-points-v2", },
|
|
|
|
{} /* Empty terminated list */
|
|
|
|
};
|
|
|
|
|
2010-07-23 17:19:35 +00:00
|
|
|
/**
|
|
|
|
* of_find_device_by_node - Find the platform_device associated with a node
|
|
|
|
* @np: Pointer to device tree node
|
|
|
|
*
|
2016-11-01 10:53:22 +00:00
|
|
|
* Takes a reference to the embedded struct device which needs to be dropped
|
|
|
|
* after use.
|
|
|
|
*
|
2021-03-25 16:47:12 +00:00
|
|
|
* Return: platform_device pointer, or NULL if not found
|
2010-07-23 17:19:35 +00:00
|
|
|
*/
|
|
|
|
struct platform_device *of_find_device_by_node(struct device_node *np)
|
|
|
|
{
|
|
|
|
struct device *dev;
|
|
|
|
|
2019-07-23 22:18:33 +00:00
|
|
|
dev = bus_find_device_by_of_node(&platform_bus_type, np);
|
2010-07-23 17:19:35 +00:00
|
|
|
return dev ? to_platform_device(dev) : NULL;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(of_find_device_by_node);
|
|
|
|
|
2024-05-29 17:49:03 +00:00
|
|
|
int of_device_add(struct platform_device *ofdev)
|
|
|
|
{
|
|
|
|
BUG_ON(ofdev->dev.of_node == NULL);
|
|
|
|
|
|
|
|
/* name and id have to be set so that the platform bus doesn't get
|
|
|
|
* confused on matching */
|
|
|
|
ofdev->name = dev_name(&ofdev->dev);
|
|
|
|
ofdev->id = PLATFORM_DEVID_NONE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If this device has not binding numa node in devicetree, that is
|
|
|
|
* of_node_to_nid returns NUMA_NO_NODE. device_add will assume that this
|
|
|
|
* device is on the same node as the parent.
|
|
|
|
*/
|
|
|
|
set_dev_node(&ofdev->dev, of_node_to_nid(ofdev->dev.of_node));
|
|
|
|
|
|
|
|
return device_add(&ofdev->dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
int of_device_register(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
device_initialize(&pdev->dev);
|
|
|
|
return of_device_add(pdev);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(of_device_register);
|
|
|
|
|
|
|
|
void of_device_unregister(struct platform_device *ofdev)
|
|
|
|
{
|
|
|
|
device_unregister(&ofdev->dev);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(of_device_unregister);
|
|
|
|
|
2012-02-24 21:58:34 +00:00
|
|
|
#ifdef CONFIG_OF_ADDRESS
|
2010-06-08 13:48:13 +00:00
|
|
|
/*
|
|
|
|
* The following routines scan a subtree and registers a device for
|
|
|
|
* each applicable node.
|
|
|
|
*
|
|
|
|
* Note: sparc doesn't use these routines because it has a different
|
|
|
|
* mechanism for creating devices from device tree nodes.
|
|
|
|
*/
|
|
|
|
|
2010-06-08 13:48:14 +00:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*
|
2014-05-21 06:40:31 +00:00
|
|
|
* 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.
|
2010-06-08 13:48:14 +00:00
|
|
|
*/
|
2016-10-17 19:17:43 +00:00
|
|
|
static void of_device_make_bus_id(struct device *dev)
|
2010-06-08 13:48:14 +00:00
|
|
|
{
|
|
|
|
struct device_node *node = dev->of_node;
|
2012-10-09 00:42:11 +00:00
|
|
|
const __be32 *reg;
|
2010-06-08 13:48:14 +00:00
|
|
|
u64 addr;
|
2022-06-30 22:34:48 +00:00
|
|
|
u32 mask;
|
2010-06-08 13:48:14 +00:00
|
|
|
|
2014-05-21 06:40:31 +00:00
|
|
|
/* 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) {
|
2022-06-30 22:34:48 +00:00
|
|
|
if (!of_property_read_u32(node, "mask", &mask))
|
|
|
|
dev_set_name(dev, dev_name(dev) ? "%llx.%x.%pOFn:%s" : "%llx.%x.%pOFn",
|
|
|
|
addr, ffs(mask) - 1, node, dev_name(dev));
|
|
|
|
|
|
|
|
else
|
|
|
|
dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn",
|
|
|
|
addr, node, dev_name(dev));
|
2010-06-08 13:48:14 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-05-21 06:40:31 +00:00
|
|
|
/* format arguments only used if dev_name() resolves to NULL */
|
|
|
|
dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s",
|
2017-06-01 23:00:00 +00:00
|
|
|
kbasename(node->full_name), dev_name(dev));
|
2014-05-21 06:40:31 +00:00
|
|
|
node = node->parent;
|
|
|
|
}
|
2010-06-08 13:48:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* of_device_alloc - Allocate and initialize an of_device
|
|
|
|
* @np: device node to assign to device
|
|
|
|
* @bus_id: Name to assign to the device. May be null to use default name.
|
|
|
|
* @parent: Parent device.
|
|
|
|
*/
|
2010-07-22 19:59:23 +00:00
|
|
|
struct platform_device *of_device_alloc(struct device_node *np,
|
2010-06-08 13:48:14 +00:00
|
|
|
const char *bus_id,
|
|
|
|
struct device *parent)
|
|
|
|
{
|
2010-07-22 19:59:23 +00:00
|
|
|
struct platform_device *dev;
|
2022-06-30 22:34:57 +00:00
|
|
|
int rc, i, num_reg = 0;
|
2024-04-30 23:50:00 +00:00
|
|
|
struct resource *res;
|
2010-06-08 13:48:15 +00:00
|
|
|
|
2017-08-25 15:33:13 +00:00
|
|
|
dev = platform_device_alloc("", PLATFORM_DEVID_NONE);
|
2010-10-20 17:45:13 +00:00
|
|
|
if (!dev)
|
|
|
|
return NULL;
|
|
|
|
|
2022-06-30 22:34:57 +00:00
|
|
|
/* count the io resources */
|
2024-04-30 23:50:00 +00:00
|
|
|
num_reg = of_address_count(np);
|
2010-06-08 13:48:15 +00:00
|
|
|
|
|
|
|
/* Populate the resource table */
|
2022-06-30 22:34:57 +00:00
|
|
|
if (num_reg) {
|
|
|
|
res = kcalloc(num_reg, sizeof(*res), GFP_KERNEL);
|
2010-10-20 17:45:13 +00:00
|
|
|
if (!res) {
|
|
|
|
platform_device_put(dev);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-06-30 22:34:57 +00:00
|
|
|
dev->num_resources = num_reg;
|
2010-06-08 13:48:15 +00:00
|
|
|
dev->resource = res;
|
|
|
|
for (i = 0; i < num_reg; i++, res++) {
|
|
|
|
rc = of_address_to_resource(np, i, res);
|
|
|
|
WARN_ON(rc);
|
|
|
|
}
|
|
|
|
}
|
2010-06-08 13:48:14 +00:00
|
|
|
|
2024-08-28 20:06:47 +00:00
|
|
|
/* setup generic device info */
|
2024-09-24 14:34:19 +00:00
|
|
|
device_set_node(&dev->dev, of_fwnode_handle(of_node_get(np)));
|
2014-11-04 10:26:26 +00:00
|
|
|
dev->dev.parent = parent ? : &platform_bus;
|
2010-06-08 13:48:14 +00:00
|
|
|
|
|
|
|
if (bus_id)
|
|
|
|
dev_set_name(&dev->dev, "%s", bus_id);
|
|
|
|
else
|
|
|
|
of_device_make_bus_id(&dev->dev);
|
|
|
|
|
|
|
|
return dev;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(of_device_alloc);
|
|
|
|
|
2010-06-08 13:48:13 +00:00
|
|
|
/**
|
2011-06-21 16:59:35 +00:00
|
|
|
* of_platform_device_create_pdata - Alloc, initialize and register an of_device
|
2010-06-08 13:48:13 +00:00
|
|
|
* @np: pointer to node to create device for
|
|
|
|
* @bus_id: name to assign device
|
2011-06-21 16:59:35 +00:00
|
|
|
* @platform_data: pointer to populate platform_data pointer with
|
2010-06-08 13:48:13 +00:00
|
|
|
* @parent: Linux device model parent device.
|
2011-01-03 22:51:11 +00:00
|
|
|
*
|
2021-03-25 16:47:12 +00:00
|
|
|
* Return: Pointer to created platform device, or NULL if a device was not
|
2011-01-03 22:51:11 +00:00
|
|
|
* registered. Unavailable devices will not get registered.
|
2010-06-08 13:48:13 +00:00
|
|
|
*/
|
2013-07-01 19:26:52 +00:00
|
|
|
static struct platform_device *of_platform_device_create_pdata(
|
2011-06-21 16:59:35 +00:00
|
|
|
struct device_node *np,
|
|
|
|
const char *bus_id,
|
|
|
|
void *platform_data,
|
|
|
|
struct device *parent)
|
2010-06-08 13:48:13 +00:00
|
|
|
{
|
2010-07-22 19:59:23 +00:00
|
|
|
struct platform_device *dev;
|
2010-06-08 13:48:13 +00:00
|
|
|
|
2014-05-15 15:55:24 +00:00
|
|
|
if (!of_device_is_available(np) ||
|
|
|
|
of_node_test_and_set_flag(np, OF_POPULATED))
|
2011-01-03 22:51:11 +00:00
|
|
|
return NULL;
|
|
|
|
|
2010-06-08 13:48:13 +00:00
|
|
|
dev = of_device_alloc(np, bus_id, parent);
|
|
|
|
if (!dev)
|
2014-05-15 15:55:24 +00:00
|
|
|
goto err_clear_flag;
|
2010-06-08 13:48:13 +00:00
|
|
|
|
2018-07-27 14:14:15 +00:00
|
|
|
dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
|
|
|
|
if (!dev->dev.dma_mask)
|
|
|
|
dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
|
2010-06-08 13:48:21 +00:00
|
|
|
dev->dev.bus = &platform_bus_type;
|
2011-06-21 16:59:35 +00:00
|
|
|
dev->dev.platform_data = platform_data;
|
2015-07-28 13:46:15 +00:00
|
|
|
of_msi_configure(&dev->dev, dev->dev.of_node);
|
2010-06-08 13:48:13 +00:00
|
|
|
|
2010-10-20 17:45:13 +00:00
|
|
|
if (of_device_add(dev) != 0) {
|
|
|
|
platform_device_put(dev);
|
2014-05-15 15:55:24 +00:00
|
|
|
goto err_clear_flag;
|
2010-06-08 13:48:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return dev;
|
2014-05-15 15:55:24 +00:00
|
|
|
|
|
|
|
err_clear_flag:
|
|
|
|
of_node_clear_flag(np, OF_POPULATED);
|
|
|
|
return NULL;
|
2010-06-08 13:48:13 +00:00
|
|
|
}
|
2011-06-21 16:59:35 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* of_platform_device_create - Alloc, initialize and register an of_device
|
|
|
|
* @np: pointer to node to create device for
|
|
|
|
* @bus_id: name to assign device
|
|
|
|
* @parent: Linux device model parent device.
|
|
|
|
*
|
2021-03-25 16:47:12 +00:00
|
|
|
* Return: Pointer to created platform device, or NULL if a device was not
|
2011-06-21 16:59:35 +00:00
|
|
|
* registered. Unavailable devices will not get registered.
|
|
|
|
*/
|
|
|
|
struct platform_device *of_platform_device_create(struct device_node *np,
|
|
|
|
const char *bus_id,
|
|
|
|
struct device *parent)
|
|
|
|
{
|
|
|
|
return of_platform_device_create_pdata(np, bus_id, NULL, parent);
|
|
|
|
}
|
2010-06-08 13:48:13 +00:00
|
|
|
EXPORT_SYMBOL(of_platform_device_create);
|
|
|
|
|
2011-06-21 16:59:34 +00:00
|
|
|
#ifdef CONFIG_ARM_AMBA
|
|
|
|
static struct amba_device *of_amba_device_create(struct device_node *node,
|
|
|
|
const char *bus_id,
|
|
|
|
void *platform_data,
|
|
|
|
struct device *parent)
|
|
|
|
{
|
|
|
|
struct amba_device *dev;
|
|
|
|
const void *prop;
|
ARM: 9119/1: amba: Properly handle device probe without IRQ domain
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2071840
Tested: This is one of a series of patch sets to enable Arm SystemReady IR
support in the kernel for NXP i.MX8 platforms. At this stage, this
has been tested by ensuring we can survive the CI/CD loop -- i.e.,
that we have not broken anything else, and a simple boot test. When
sufficient drivers have been brought in for i.MX8M, we will be able
to run further tests.
commit 854f695c3d41853eb7efcd436023c5ab92a257eb
Author: Wang Kefeng <wangkefeng.wang@huawei.com>
Date: Mon Aug 23 10:41:43 2021 +0100
ARM: 9119/1: amba: Properly handle device probe without IRQ domain
of_amba_device_create() uses irq_of_parse_and_map() to translate
a DT interrupt specification into a Linux virtual interrupt number.
But it doesn't properly handle the case where the interrupt controller
is not yet available, eg, when pl011 interrupt is connected to MBIGEN
interrupt controller, because the mbigen initialization is too late,
which will lead to no IRQ due to no IRQ domain found, log is shown below,
"irq: no irq domain found for uart0 !"
use of_irq_get() to return -EPROBE_DEFER as above, and in the function
amba_device_try_add()/amba_device_add(), it will properly handle in such
case, also return 0 in other fail cases to be consistent as before.
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Frank Rowand <frowand.list@gmail.com>
Reported-by: Ruizhe Lin <linruizhe@huawei.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Kefeng Wang <wangkefeng.wang@huawei.com>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
(cherry picked from commit 854f695c3d41853eb7efcd436023c5ab92a257eb)
Signed-off-by: Al Stone <ahs3@redhat.com>
2022-06-30 22:34:43 +00:00
|
|
|
int ret;
|
2011-06-21 16:59:34 +00:00
|
|
|
|
2017-06-01 20:50:55 +00:00
|
|
|
pr_debug("Creating amba device %pOF\n", node);
|
2011-06-21 16:59:34 +00:00
|
|
|
|
2014-05-15 15:55:24 +00:00
|
|
|
if (!of_device_is_available(node) ||
|
|
|
|
of_node_test_and_set_flag(node, OF_POPULATED))
|
2011-06-21 16:59:34 +00:00
|
|
|
return NULL;
|
|
|
|
|
2011-12-18 11:45:17 +00:00
|
|
|
dev = amba_device_alloc(NULL, 0, 0);
|
2016-06-15 13:32:18 +00:00
|
|
|
if (!dev)
|
2014-05-15 15:55:24 +00:00
|
|
|
goto err_clear_flag;
|
2011-06-21 16:59:34 +00:00
|
|
|
|
2018-08-31 14:13:07 +00:00
|
|
|
/* AMBA devices only support a single DMA mask */
|
|
|
|
dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
|
|
|
|
dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
|
|
|
|
|
2011-06-21 16:59:34 +00:00
|
|
|
/* setup generic device info */
|
2024-09-24 14:34:19 +00:00
|
|
|
device_set_node(&dev->dev, of_fwnode_handle(node));
|
2014-11-04 10:26:26 +00:00
|
|
|
dev->dev.parent = parent ? : &platform_bus;
|
2011-06-21 16:59:34 +00:00
|
|
|
dev->dev.platform_data = platform_data;
|
|
|
|
if (bus_id)
|
|
|
|
dev_set_name(&dev->dev, "%s", bus_id);
|
|
|
|
else
|
|
|
|
of_device_make_bus_id(&dev->dev);
|
|
|
|
|
|
|
|
/* Allow the HW Peripheral ID to be overridden */
|
|
|
|
prop = of_get_property(node, "arm,primecell-periphid", NULL);
|
|
|
|
if (prop)
|
|
|
|
dev->periphid = of_read_ulong(prop, 1);
|
|
|
|
|
|
|
|
ret = of_address_to_resource(node, 0, &dev->res);
|
2013-08-30 11:17:29 +00:00
|
|
|
if (ret) {
|
2017-06-01 20:50:55 +00:00
|
|
|
pr_err("amba: of_address_to_resource() failed (%d) for %pOF\n",
|
|
|
|
ret, node);
|
2011-06-21 16:59:34 +00:00
|
|
|
goto err_free;
|
2013-08-30 11:17:29 +00:00
|
|
|
}
|
2011-06-21 16:59:34 +00:00
|
|
|
|
2011-12-18 11:45:17 +00:00
|
|
|
ret = amba_device_add(dev, &iomem_resource);
|
2013-08-30 11:17:29 +00:00
|
|
|
if (ret) {
|
2017-06-01 20:50:55 +00:00
|
|
|
pr_err("amba_device_add() failed (%d) for %pOF\n",
|
|
|
|
ret, node);
|
2011-06-21 16:59:34 +00:00
|
|
|
goto err_free;
|
2013-08-30 11:17:29 +00:00
|
|
|
}
|
2011-06-21 16:59:34 +00:00
|
|
|
|
|
|
|
return dev;
|
|
|
|
|
|
|
|
err_free:
|
2011-12-18 11:45:17 +00:00
|
|
|
amba_device_put(dev);
|
2014-05-15 15:55:24 +00:00
|
|
|
err_clear_flag:
|
|
|
|
of_node_clear_flag(node, OF_POPULATED);
|
2011-06-21 16:59:34 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#else /* CONFIG_ARM_AMBA */
|
|
|
|
static struct amba_device *of_amba_device_create(struct device_node *node,
|
|
|
|
const char *bus_id,
|
|
|
|
void *platform_data,
|
|
|
|
struct device *parent)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_ARM_AMBA */
|
|
|
|
|
2021-03-18 10:40:29 +00:00
|
|
|
/*
|
2020-05-24 15:26:14 +00:00
|
|
|
* of_dev_lookup() - Given a device node, lookup the preferred Linux name
|
2011-06-21 16:59:35 +00:00
|
|
|
*/
|
|
|
|
static const struct of_dev_auxdata *of_dev_lookup(const struct of_dev_auxdata *lookup,
|
|
|
|
struct device_node *np)
|
|
|
|
{
|
of/platform: Allow secondary compatible match in of_dev_lookup
We currently try to match of_dev_auxdata based on compatible,
IO address, and device name. But in some cases we have multiple
instances of drivers that can use the same auxdata.
Let's add an additional secondary lookup for generic compatible
match for auxdata if no device specific match is found. This does
not change the existing matching, and still allows adding device
specific auxdata.
This simplifies things as specifying the IO address and device
name is prone errors as it requires maintaining an in kernel
database for each SoC.
To specify a generic match, all that is needed is to add a
OF_DEV_AUXDATA entry with no device instance specified:
OF_DEV_AUXDATA("pinctrl-single", 0, NULL, &pcs_pdata),
As the auxdata is already initialized only for the booted SoC,
there's not much of a chance of getting things wrong.
Let's also fix two checkpatch warnings while at it to add a
space before parenthesis in the for loop, and remove a comparison
to NULL by using !auxdata->compatible.
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Tony Lindgren <tony@atomide.com>
2016-04-15 15:45:32 +00:00
|
|
|
const struct of_dev_auxdata *auxdata;
|
2011-06-21 16:59:35 +00:00
|
|
|
struct resource res;
|
of/platform: Allow secondary compatible match in of_dev_lookup
We currently try to match of_dev_auxdata based on compatible,
IO address, and device name. But in some cases we have multiple
instances of drivers that can use the same auxdata.
Let's add an additional secondary lookup for generic compatible
match for auxdata if no device specific match is found. This does
not change the existing matching, and still allows adding device
specific auxdata.
This simplifies things as specifying the IO address and device
name is prone errors as it requires maintaining an in kernel
database for each SoC.
To specify a generic match, all that is needed is to add a
OF_DEV_AUXDATA entry with no device instance specified:
OF_DEV_AUXDATA("pinctrl-single", 0, NULL, &pcs_pdata),
As the auxdata is already initialized only for the booted SoC,
there's not much of a chance of getting things wrong.
Let's also fix two checkpatch warnings while at it to add a
space before parenthesis in the for loop, and remove a comparison
to NULL by using !auxdata->compatible.
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Tony Lindgren <tony@atomide.com>
2016-04-15 15:45:32 +00:00
|
|
|
int compatible = 0;
|
2011-11-03 05:07:29 +00:00
|
|
|
|
|
|
|
if (!lookup)
|
|
|
|
return NULL;
|
|
|
|
|
of/platform: Allow secondary compatible match in of_dev_lookup
We currently try to match of_dev_auxdata based on compatible,
IO address, and device name. But in some cases we have multiple
instances of drivers that can use the same auxdata.
Let's add an additional secondary lookup for generic compatible
match for auxdata if no device specific match is found. This does
not change the existing matching, and still allows adding device
specific auxdata.
This simplifies things as specifying the IO address and device
name is prone errors as it requires maintaining an in kernel
database for each SoC.
To specify a generic match, all that is needed is to add a
OF_DEV_AUXDATA entry with no device instance specified:
OF_DEV_AUXDATA("pinctrl-single", 0, NULL, &pcs_pdata),
As the auxdata is already initialized only for the booted SoC,
there's not much of a chance of getting things wrong.
Let's also fix two checkpatch warnings while at it to add a
space before parenthesis in the for loop, and remove a comparison
to NULL by using !auxdata->compatible.
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Tony Lindgren <tony@atomide.com>
2016-04-15 15:45:32 +00:00
|
|
|
auxdata = lookup;
|
|
|
|
for (; auxdata->compatible; auxdata++) {
|
|
|
|
if (!of_device_is_compatible(np, auxdata->compatible))
|
2011-11-03 05:07:29 +00:00
|
|
|
continue;
|
of/platform: Allow secondary compatible match in of_dev_lookup
We currently try to match of_dev_auxdata based on compatible,
IO address, and device name. But in some cases we have multiple
instances of drivers that can use the same auxdata.
Let's add an additional secondary lookup for generic compatible
match for auxdata if no device specific match is found. This does
not change the existing matching, and still allows adding device
specific auxdata.
This simplifies things as specifying the IO address and device
name is prone errors as it requires maintaining an in kernel
database for each SoC.
To specify a generic match, all that is needed is to add a
OF_DEV_AUXDATA entry with no device instance specified:
OF_DEV_AUXDATA("pinctrl-single", 0, NULL, &pcs_pdata),
As the auxdata is already initialized only for the booted SoC,
there's not much of a chance of getting things wrong.
Let's also fix two checkpatch warnings while at it to add a
space before parenthesis in the for loop, and remove a comparison
to NULL by using !auxdata->compatible.
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Tony Lindgren <tony@atomide.com>
2016-04-15 15:45:32 +00:00
|
|
|
compatible++;
|
2012-07-05 14:15:36 +00:00
|
|
|
if (!of_address_to_resource(np, 0, &res))
|
of/platform: Allow secondary compatible match in of_dev_lookup
We currently try to match of_dev_auxdata based on compatible,
IO address, and device name. But in some cases we have multiple
instances of drivers that can use the same auxdata.
Let's add an additional secondary lookup for generic compatible
match for auxdata if no device specific match is found. This does
not change the existing matching, and still allows adding device
specific auxdata.
This simplifies things as specifying the IO address and device
name is prone errors as it requires maintaining an in kernel
database for each SoC.
To specify a generic match, all that is needed is to add a
OF_DEV_AUXDATA entry with no device instance specified:
OF_DEV_AUXDATA("pinctrl-single", 0, NULL, &pcs_pdata),
As the auxdata is already initialized only for the booted SoC,
there's not much of a chance of getting things wrong.
Let's also fix two checkpatch warnings while at it to add a
space before parenthesis in the for loop, and remove a comparison
to NULL by using !auxdata->compatible.
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Tony Lindgren <tony@atomide.com>
2016-04-15 15:45:32 +00:00
|
|
|
if (res.start != auxdata->phys_addr)
|
2012-07-05 14:15:36 +00:00
|
|
|
continue;
|
2017-06-01 20:50:55 +00:00
|
|
|
pr_debug("%pOF: devname=%s\n", np, auxdata->name);
|
of/platform: Allow secondary compatible match in of_dev_lookup
We currently try to match of_dev_auxdata based on compatible,
IO address, and device name. But in some cases we have multiple
instances of drivers that can use the same auxdata.
Let's add an additional secondary lookup for generic compatible
match for auxdata if no device specific match is found. This does
not change the existing matching, and still allows adding device
specific auxdata.
This simplifies things as specifying the IO address and device
name is prone errors as it requires maintaining an in kernel
database for each SoC.
To specify a generic match, all that is needed is to add a
OF_DEV_AUXDATA entry with no device instance specified:
OF_DEV_AUXDATA("pinctrl-single", 0, NULL, &pcs_pdata),
As the auxdata is already initialized only for the booted SoC,
there's not much of a chance of getting things wrong.
Let's also fix two checkpatch warnings while at it to add a
space before parenthesis in the for loop, and remove a comparison
to NULL by using !auxdata->compatible.
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Tony Lindgren <tony@atomide.com>
2016-04-15 15:45:32 +00:00
|
|
|
return auxdata;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!compatible)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Try compatible match if no phys_addr and name are specified */
|
|
|
|
auxdata = lookup;
|
|
|
|
for (; auxdata->compatible; auxdata++) {
|
|
|
|
if (!of_device_is_compatible(np, auxdata->compatible))
|
|
|
|
continue;
|
|
|
|
if (!auxdata->phys_addr && !auxdata->name) {
|
2017-06-01 20:50:55 +00:00
|
|
|
pr_debug("%pOF: compatible match\n", np);
|
of/platform: Allow secondary compatible match in of_dev_lookup
We currently try to match of_dev_auxdata based on compatible,
IO address, and device name. But in some cases we have multiple
instances of drivers that can use the same auxdata.
Let's add an additional secondary lookup for generic compatible
match for auxdata if no device specific match is found. This does
not change the existing matching, and still allows adding device
specific auxdata.
This simplifies things as specifying the IO address and device
name is prone errors as it requires maintaining an in kernel
database for each SoC.
To specify a generic match, all that is needed is to add a
OF_DEV_AUXDATA entry with no device instance specified:
OF_DEV_AUXDATA("pinctrl-single", 0, NULL, &pcs_pdata),
As the auxdata is already initialized only for the booted SoC,
there's not much of a chance of getting things wrong.
Let's also fix two checkpatch warnings while at it to add a
space before parenthesis in the for loop, and remove a comparison
to NULL by using !auxdata->compatible.
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Tony Lindgren <tony@atomide.com>
2016-04-15 15:45:32 +00:00
|
|
|
return auxdata;
|
|
|
|
}
|
2011-06-21 16:59:35 +00:00
|
|
|
}
|
2011-11-03 05:07:29 +00:00
|
|
|
|
2011-06-21 16:59:35 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-06-08 13:48:13 +00:00
|
|
|
/**
|
2011-03-18 16:21:28 +00:00
|
|
|
* of_platform_bus_create() - Create a device for a node and its children.
|
2010-06-08 13:48:13 +00:00
|
|
|
* @bus: device node of the bus to instantiate
|
2011-03-18 16:21:29 +00:00
|
|
|
* @matches: match table for bus nodes
|
2011-11-03 05:07:29 +00:00
|
|
|
* @lookup: auxdata table for matching id and platform_data with device nodes
|
2011-03-18 16:21:28 +00:00
|
|
|
* @parent: parent for new device, or NULL for top level.
|
2011-11-03 05:07:29 +00:00
|
|
|
* @strict: require compatible property
|
2011-03-18 16:21:28 +00:00
|
|
|
*
|
|
|
|
* Creates a platform_device for the provided device_node, and optionally
|
|
|
|
* recursively create devices for all the child nodes.
|
2010-06-08 13:48:13 +00:00
|
|
|
*/
|
2011-03-18 16:21:28 +00:00
|
|
|
static int of_platform_bus_create(struct device_node *bus,
|
2010-06-08 13:48:13 +00:00
|
|
|
const struct of_device_id *matches,
|
2011-06-21 16:59:35 +00:00
|
|
|
const struct of_dev_auxdata *lookup,
|
2011-06-21 16:59:34 +00:00
|
|
|
struct device *parent, bool strict)
|
2010-06-08 13:48:13 +00:00
|
|
|
{
|
2011-06-21 16:59:35 +00:00
|
|
|
const struct of_dev_auxdata *auxdata;
|
2010-06-08 13:48:13 +00:00
|
|
|
struct device_node *child;
|
2010-07-22 19:59:23 +00:00
|
|
|
struct platform_device *dev;
|
2011-06-21 16:59:35 +00:00
|
|
|
const char *bus_id = NULL;
|
|
|
|
void *platform_data = NULL;
|
2010-06-08 13:48:13 +00:00
|
|
|
int rc = 0;
|
|
|
|
|
2011-06-21 16:59:34 +00:00
|
|
|
/* Make sure it has a compatible property */
|
|
|
|
if (strict && (!of_get_property(bus, "compatible", NULL))) {
|
2017-06-01 20:50:55 +00:00
|
|
|
pr_debug("%s() - skipping %pOF, no compatible prop\n",
|
|
|
|
__func__, bus);
|
2011-06-21 16:59:34 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-04-17 06:27:34 +00:00
|
|
|
/* Skip nodes for which we don't want to create devices */
|
|
|
|
if (unlikely(of_match_node(of_skipped_node_table, bus))) {
|
|
|
|
pr_debug("%s() - skipping %pOF node\n", __func__, bus);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-01 06:52:54 +00:00
|
|
|
if (of_node_check_flag(bus, OF_POPULATED_BUS)) {
|
2017-06-01 20:50:55 +00:00
|
|
|
pr_debug("%s() - skipping %pOF, already populated\n",
|
|
|
|
__func__, bus);
|
2016-06-01 06:52:54 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-06-21 16:59:35 +00:00
|
|
|
auxdata = of_dev_lookup(lookup, bus);
|
|
|
|
if (auxdata) {
|
|
|
|
bus_id = auxdata->name;
|
|
|
|
platform_data = auxdata->platform_data;
|
|
|
|
}
|
|
|
|
|
2011-06-21 16:59:34 +00:00
|
|
|
if (of_device_is_compatible(bus, "arm,primecell")) {
|
2013-08-30 11:17:29 +00:00
|
|
|
/*
|
|
|
|
* Don't return an error here to keep compatibility with older
|
|
|
|
* device tree files.
|
|
|
|
*/
|
2011-06-21 16:59:35 +00:00
|
|
|
of_amba_device_create(bus, bus_id, platform_data, parent);
|
2011-06-21 16:59:34 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-06-21 16:59:35 +00:00
|
|
|
dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
|
2011-03-18 16:21:28 +00:00
|
|
|
if (!dev || !of_match_node(matches, bus))
|
|
|
|
return 0;
|
|
|
|
|
2010-06-08 13:48:13 +00:00
|
|
|
for_each_child_of_node(bus, child) {
|
2017-06-01 20:50:55 +00:00
|
|
|
pr_debug(" create child: %pOF\n", child);
|
2011-06-21 16:59:35 +00:00
|
|
|
rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
|
2010-06-08 13:48:13 +00:00
|
|
|
if (rc) {
|
|
|
|
of_node_put(child);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-06-24 15:13:47 +00:00
|
|
|
of_node_set_flag(bus, OF_POPULATED_BUS);
|
2010-06-08 13:48:13 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-03-18 16:21:28 +00:00
|
|
|
* of_platform_bus_probe() - Probe the device-tree for platform buses
|
2010-06-08 13:48:13 +00:00
|
|
|
* @root: parent of the first level to probe or NULL for the root of the tree
|
2011-03-18 16:21:29 +00:00
|
|
|
* @matches: match table for bus nodes
|
2010-06-08 13:48:13 +00:00
|
|
|
* @parent: parent to hook devices from, NULL for toplevel
|
|
|
|
*
|
|
|
|
* Note that children of the provided root are not instantiated as devices
|
|
|
|
* unless the specified root itself matches the bus list and is not NULL.
|
|
|
|
*/
|
|
|
|
int of_platform_bus_probe(struct device_node *root,
|
|
|
|
const struct of_device_id *matches,
|
|
|
|
struct device *parent)
|
|
|
|
{
|
|
|
|
struct device_node *child;
|
|
|
|
int rc = 0;
|
|
|
|
|
2011-03-18 16:21:29 +00:00
|
|
|
root = root ? of_node_get(root) : of_find_node_by_path("/");
|
|
|
|
if (!root)
|
2010-06-08 13:48:25 +00:00
|
|
|
return -EINVAL;
|
2010-06-08 13:48:13 +00:00
|
|
|
|
2016-06-01 06:52:54 +00:00
|
|
|
pr_debug("%s()\n", __func__);
|
2017-06-01 20:50:55 +00:00
|
|
|
pr_debug(" starting at: %pOF\n", root);
|
2010-06-08 13:48:13 +00:00
|
|
|
|
2011-03-18 16:21:29 +00:00
|
|
|
/* Do a self check of bus type, if there's a match, create children */
|
2010-06-08 13:48:13 +00:00
|
|
|
if (of_match_node(matches, root)) {
|
2011-06-21 16:59:35 +00:00
|
|
|
rc = of_platform_bus_create(root, matches, NULL, parent, false);
|
2011-03-18 16:21:28 +00:00
|
|
|
} else for_each_child_of_node(root, child) {
|
2010-06-08 13:48:13 +00:00
|
|
|
if (!of_match_node(matches, child))
|
|
|
|
continue;
|
2011-06-21 16:59:35 +00:00
|
|
|
rc = of_platform_bus_create(child, matches, NULL, parent, false);
|
2015-10-22 09:02:49 +00:00
|
|
|
if (rc) {
|
|
|
|
of_node_put(child);
|
2010-06-08 13:48:13 +00:00
|
|
|
break;
|
2015-10-22 09:02:49 +00:00
|
|
|
}
|
2010-06-08 13:48:13 +00:00
|
|
|
}
|
2011-03-18 16:21:28 +00:00
|
|
|
|
2010-06-08 13:48:13 +00:00
|
|
|
of_node_put(root);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(of_platform_bus_probe);
|
2011-06-21 16:59:34 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* of_platform_populate() - Populate platform_devices from device tree data
|
|
|
|
* @root: parent of the first level to probe or NULL for the root of the tree
|
|
|
|
* @matches: match table, NULL to use the default
|
2012-11-23 16:25:08 +00:00
|
|
|
* @lookup: auxdata table for matching id and platform_data with device nodes
|
2011-06-21 16:59:34 +00:00
|
|
|
* @parent: parent to hook devices from, NULL for toplevel
|
|
|
|
*
|
|
|
|
* Similar to of_platform_bus_probe(), this function walks the device tree
|
|
|
|
* and creates devices from nodes. It differs in that it follows the modern
|
|
|
|
* convention of requiring all device nodes to have a 'compatible' property,
|
|
|
|
* and it is suitable for creating devices which are children of the root
|
|
|
|
* node (of_platform_bus_probe will only create children of the root which
|
|
|
|
* are selected by the @matches argument).
|
|
|
|
*
|
|
|
|
* New board support should be using this function instead of
|
|
|
|
* of_platform_bus_probe().
|
|
|
|
*
|
2021-03-25 16:47:12 +00:00
|
|
|
* Return: 0 on success, < 0 on failure.
|
2011-06-21 16:59:34 +00:00
|
|
|
*/
|
|
|
|
int of_platform_populate(struct device_node *root,
|
|
|
|
const struct of_device_id *matches,
|
2011-06-21 16:59:35 +00:00
|
|
|
const struct of_dev_auxdata *lookup,
|
2011-06-21 16:59:34 +00:00
|
|
|
struct device *parent)
|
|
|
|
{
|
|
|
|
struct device_node *child;
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
root = root ? of_node_get(root) : of_find_node_by_path("/");
|
|
|
|
if (!root)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2016-06-01 06:52:54 +00:00
|
|
|
pr_debug("%s()\n", __func__);
|
2017-06-01 20:50:55 +00:00
|
|
|
pr_debug(" starting at: %pOF\n", root);
|
2016-06-01 06:52:54 +00:00
|
|
|
|
2019-09-04 21:11:24 +00:00
|
|
|
device_links_supplier_sync_state_pause();
|
2011-06-21 16:59:34 +00:00
|
|
|
for_each_child_of_node(root, child) {
|
2011-06-21 16:59:35 +00:00
|
|
|
rc = of_platform_bus_create(child, matches, lookup, parent, true);
|
2015-10-22 09:02:49 +00:00
|
|
|
if (rc) {
|
|
|
|
of_node_put(child);
|
2011-06-21 16:59:34 +00:00
|
|
|
break;
|
2015-10-22 09:02:49 +00:00
|
|
|
}
|
2011-06-21 16:59:34 +00:00
|
|
|
}
|
2019-09-04 21:11:24 +00:00
|
|
|
device_links_supplier_sync_state_resume();
|
|
|
|
|
2014-11-19 22:35:39 +00:00
|
|
|
of_node_set_flag(root, OF_POPULATED_BUS);
|
2011-06-21 16:59:34 +00:00
|
|
|
|
|
|
|
of_node_put(root);
|
|
|
|
return rc;
|
|
|
|
}
|
2012-06-07 23:57:36 +00:00
|
|
|
EXPORT_SYMBOL_GPL(of_platform_populate);
|
2014-05-15 15:55:24 +00:00
|
|
|
|
2015-08-02 17:44:43 +00:00
|
|
|
int of_platform_default_populate(struct device_node *root,
|
|
|
|
const struct of_dev_auxdata *lookup,
|
|
|
|
struct device *parent)
|
|
|
|
{
|
|
|
|
return of_platform_populate(root, of_default_bus_match_table, lookup,
|
|
|
|
parent);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(of_platform_default_populate);
|
|
|
|
|
2017-10-11 05:08:53 +00:00
|
|
|
static const struct of_device_id reserved_mem_matches[] = {
|
2022-06-30 22:34:59 +00:00
|
|
|
{ .compatible = "phram" },
|
2017-10-16 18:17:08 +00:00
|
|
|
{ .compatible = "qcom,rmtfs-mem" },
|
2018-04-10 17:57:23 +00:00
|
|
|
{ .compatible = "qcom,cmd-db" },
|
2022-06-30 22:34:42 +00:00
|
|
|
{ .compatible = "qcom,smem" },
|
2017-10-11 05:08:53 +00:00
|
|
|
{ .compatible = "ramoops" },
|
2021-01-29 17:14:29 +00:00
|
|
|
{ .compatible = "nvmem-rmem" },
|
2022-06-30 22:34:55 +00:00
|
|
|
{ .compatible = "google,open-dice" },
|
2017-10-11 05:08:53 +00:00
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
2016-06-01 06:52:54 +00:00
|
|
|
static int __init of_platform_default_populate_init(void)
|
|
|
|
{
|
2016-07-30 01:11:32 +00:00
|
|
|
struct device_node *node;
|
|
|
|
|
2019-12-09 19:31:19 +00:00
|
|
|
device_links_supplier_sync_state_pause();
|
|
|
|
|
2016-07-30 01:11:32 +00:00
|
|
|
if (!of_have_populated_dt())
|
|
|
|
return -ENODEV;
|
|
|
|
|
2022-06-30 22:34:58 +00:00
|
|
|
if (IS_ENABLED(CONFIG_PPC)) {
|
|
|
|
struct device_node *boot_display = NULL;
|
|
|
|
struct platform_device *dev;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Check if we have a MacOS display without a node spec */
|
|
|
|
if (of_get_property(of_chosen, "linux,bootx-noscreen", NULL)) {
|
|
|
|
/*
|
|
|
|
* The old code tried to work out which node was the MacOS
|
|
|
|
* display based on the address. I'm dropping that since the
|
|
|
|
* lack of a node spec only happens with old BootX versions
|
|
|
|
* (users can update) and with this code, they'll still get
|
|
|
|
* a display (just not the palette hacks).
|
|
|
|
*/
|
|
|
|
dev = platform_device_alloc("bootx-noscreen", 0);
|
|
|
|
if (WARN_ON(!dev))
|
|
|
|
return -ENOMEM;
|
|
|
|
ret = platform_device_add(dev);
|
|
|
|
if (WARN_ON(ret)) {
|
|
|
|
platform_device_put(dev);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
2016-07-30 01:11:32 +00:00
|
|
|
|
2022-06-30 22:34:58 +00:00
|
|
|
/*
|
|
|
|
* For OF framebuffers, first create the device for the boot display,
|
|
|
|
* then for the other framebuffers. Only fail for the boot display;
|
|
|
|
* ignore errors for the rest.
|
|
|
|
*/
|
|
|
|
for_each_node_by_type(node, "display") {
|
|
|
|
if (!of_get_property(node, "linux,opened", NULL) ||
|
|
|
|
!of_get_property(node, "linux,boot-display", NULL))
|
|
|
|
continue;
|
|
|
|
dev = of_platform_device_create(node, "of-display", NULL);
|
|
|
|
if (WARN_ON(!dev))
|
|
|
|
return -ENOMEM;
|
|
|
|
boot_display = node;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
for_each_node_by_type(node, "display") {
|
|
|
|
if (!of_get_property(node, "linux,opened", NULL) || node == boot_display)
|
|
|
|
continue;
|
|
|
|
of_platform_device_create(node, "of-display", NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Handle certain compatibles explicitly, since we don't want to create
|
|
|
|
* platform_devices for every node in /reserved-memory with a
|
|
|
|
* "compatible",
|
|
|
|
*/
|
|
|
|
for_each_matching_node(node, reserved_mem_matches)
|
|
|
|
of_platform_device_create(node, NULL, NULL);
|
|
|
|
|
|
|
|
node = of_find_node_by_path("/firmware");
|
|
|
|
if (node) {
|
|
|
|
of_platform_populate(node, NULL, NULL, NULL);
|
|
|
|
of_node_put(node);
|
|
|
|
}
|
2017-09-28 10:45:59 +00:00
|
|
|
|
2022-06-30 22:34:58 +00:00
|
|
|
node = of_get_compatible_child(of_chosen, "simple-framebuffer");
|
of/platform: Disable sysfb if a simple-framebuffer node is found
JIRA: https://issues.redhat.com/browse/RHEL-53899
Upstream Status: v6.8-rc1
commit 3310288f61357b9b54139c93fc7665d60c0288bf
Author: Javier Martinez Canillas <javierm@redhat.com>
AuthorDate: Mon Nov 13 09:51:41 2023 +0100
Commit: Rob Herring <robh@kernel.org>
CommitDate: Thu Dec 7 11:30:06 2023 -0600
Some DT platforms use EFI to boot and in this case the EFI Boot Services
may register a EFI_GRAPHICS_OUTPUT_PROTOCOL handle, that will later be
queried by the Linux EFI stub to fill the global struct screen_info data.
The data is used by the Generic System Framebuffers (sysfb) framework to
add a platform device with platform data about the system framebuffer.
But if there is a "simple-framebuffer" node in the DT, the OF core will
also do the same and add another device for the system framebuffer.
This could lead for example, to two platform devices ("simple-framebuffer"
and "efi-framebuffer") to be added and matched with their corresponding
drivers. So both efifb and simpledrm will be probed, leading to following:
[ 0.055752] efifb: framebuffer at 0xbd58dc000, using 16000k, total 16000k
[ 0.055755] efifb: mode is 2560x1600x32, linelength=10240, pages=1
[ 0.055758] efifb: scrolling: redraw
[ 0.055759] efifb: Truecolor: size=2:10:10:10, shift=30:20:10:0
...
[ 3.295896] simple-framebuffer bd58dc000.framebuffer: [drm] *ERROR*
could not acquire memory range [??? 0xffff79f30a29ee40-0x2a5000001a7
flags 0x0]: -16
[ 3.298018] simple-framebuffer: probe of bd58dc000.framebuffer
failed with error -16
To prevent the issue, make the OF core to disable sysfb if there is a node
with a "simple-framebuffer" compatible. That way only this device will be
registered and sysfb would not attempt to register another one using the
screen_info data even if this has been filled.
This seems the correct thing to do in this case because:
a) On a DT platform, the DTB is the single source of truth since is what
describes the hardware topology. Even if EFI Boot Services are used to
boot the machine.
b) The of_platform_default_populate_init() function is called in the
arch_initcall_sync() initcall level while the sysfb_init() function
is called later in the subsys_initcall() initcall level.
Reported-by: Andrew Worsley <amworsley@gmail.com>
Closes: https://lore.kernel.org/all/20231111042926.52990-2-amworsley@gmail.com
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
Reviewed-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://lore.kernel.org/r/20231113085305.1823455-1-javierm@redhat.com
Signed-off-by: Rob Herring <robh@kernel.org>
Signed-off-by: Jocelyn Falempe <jfalempe@redhat.com>
2024-11-07 10:31:09 +00:00
|
|
|
if (node) {
|
|
|
|
/*
|
|
|
|
* Since a "simple-framebuffer" device is already added
|
|
|
|
* here, disable the Generic System Framebuffers (sysfb)
|
|
|
|
* to prevent it from registering another device for the
|
|
|
|
* system framebuffer later (e.g: using the screen_info
|
|
|
|
* data that may had been filled as well).
|
|
|
|
*
|
|
|
|
* This can happen for example on DT systems that do EFI
|
|
|
|
* booting and may provide a GOP handle to the EFI stub.
|
|
|
|
*/
|
2024-11-07 11:07:36 +00:00
|
|
|
sysfb_disable(NULL);
|
of/platform: Disable sysfb if a simple-framebuffer node is found
JIRA: https://issues.redhat.com/browse/RHEL-53899
Upstream Status: v6.8-rc1
commit 3310288f61357b9b54139c93fc7665d60c0288bf
Author: Javier Martinez Canillas <javierm@redhat.com>
AuthorDate: Mon Nov 13 09:51:41 2023 +0100
Commit: Rob Herring <robh@kernel.org>
CommitDate: Thu Dec 7 11:30:06 2023 -0600
Some DT platforms use EFI to boot and in this case the EFI Boot Services
may register a EFI_GRAPHICS_OUTPUT_PROTOCOL handle, that will later be
queried by the Linux EFI stub to fill the global struct screen_info data.
The data is used by the Generic System Framebuffers (sysfb) framework to
add a platform device with platform data about the system framebuffer.
But if there is a "simple-framebuffer" node in the DT, the OF core will
also do the same and add another device for the system framebuffer.
This could lead for example, to two platform devices ("simple-framebuffer"
and "efi-framebuffer") to be added and matched with their corresponding
drivers. So both efifb and simpledrm will be probed, leading to following:
[ 0.055752] efifb: framebuffer at 0xbd58dc000, using 16000k, total 16000k
[ 0.055755] efifb: mode is 2560x1600x32, linelength=10240, pages=1
[ 0.055758] efifb: scrolling: redraw
[ 0.055759] efifb: Truecolor: size=2:10:10:10, shift=30:20:10:0
...
[ 3.295896] simple-framebuffer bd58dc000.framebuffer: [drm] *ERROR*
could not acquire memory range [??? 0xffff79f30a29ee40-0x2a5000001a7
flags 0x0]: -16
[ 3.298018] simple-framebuffer: probe of bd58dc000.framebuffer
failed with error -16
To prevent the issue, make the OF core to disable sysfb if there is a node
with a "simple-framebuffer" compatible. That way only this device will be
registered and sysfb would not attempt to register another one using the
screen_info data even if this has been filled.
This seems the correct thing to do in this case because:
a) On a DT platform, the DTB is the single source of truth since is what
describes the hardware topology. Even if EFI Boot Services are used to
boot the machine.
b) The of_platform_default_populate_init() function is called in the
arch_initcall_sync() initcall level while the sysfb_init() function
is called later in the subsys_initcall() initcall level.
Reported-by: Andrew Worsley <amworsley@gmail.com>
Closes: https://lore.kernel.org/all/20231111042926.52990-2-amworsley@gmail.com
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
Reviewed-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://lore.kernel.org/r/20231113085305.1823455-1-javierm@redhat.com
Signed-off-by: Rob Herring <robh@kernel.org>
Signed-off-by: Jocelyn Falempe <jfalempe@redhat.com>
2024-11-07 10:31:09 +00:00
|
|
|
of_platform_device_create(node, NULL, NULL);
|
|
|
|
of_node_put(node);
|
|
|
|
}
|
2022-06-30 22:34:50 +00:00
|
|
|
|
2022-06-30 22:34:58 +00:00
|
|
|
/* Populate everything else. */
|
|
|
|
of_platform_default_populate(NULL, NULL, NULL);
|
|
|
|
}
|
2016-06-01 06:52:54 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
arch_initcall_sync(of_platform_default_populate_init);
|
2019-09-04 21:11:24 +00:00
|
|
|
|
|
|
|
static int __init of_platform_sync_state_init(void)
|
|
|
|
{
|
2019-12-09 19:31:19 +00:00
|
|
|
device_links_supplier_sync_state_resume();
|
2019-09-04 21:11:24 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
late_initcall_sync(of_platform_sync_state_init);
|
2016-06-01 06:52:54 +00:00
|
|
|
|
2017-05-22 11:09:20 +00:00
|
|
|
int of_platform_device_destroy(struct device *dev, void *data)
|
2014-05-15 15:55:24 +00:00
|
|
|
{
|
|
|
|
/* Do not touch devices not populated from the device tree */
|
2014-06-24 15:13:47 +00:00
|
|
|
if (!dev->of_node || !of_node_check_flag(dev->of_node, OF_POPULATED))
|
2014-05-15 15:55:24 +00:00
|
|
|
return 0;
|
|
|
|
|
2014-06-24 15:13:47 +00:00
|
|
|
/* Recurse for any nodes that were treated as busses */
|
|
|
|
if (of_node_check_flag(dev->of_node, OF_POPULATED_BUS))
|
|
|
|
device_for_each_child(dev, NULL, of_platform_device_destroy);
|
2014-05-15 15:55:24 +00:00
|
|
|
|
2018-06-04 14:14:08 +00:00
|
|
|
of_node_clear_flag(dev->of_node, OF_POPULATED);
|
|
|
|
of_node_clear_flag(dev->of_node, OF_POPULATED_BUS);
|
|
|
|
|
2014-05-15 15:55:24 +00:00
|
|
|
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
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2017-05-22 11:09:20 +00:00
|
|
|
EXPORT_SYMBOL_GPL(of_platform_device_destroy);
|
2014-05-15 15:55:24 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* of_platform_depopulate() - Remove devices populated from device tree
|
2014-06-24 15:13:47 +00:00
|
|
|
* @parent: device which children will be removed
|
2014-05-15 15:55:24 +00:00
|
|
|
*
|
|
|
|
* 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).
|
|
|
|
*/
|
2014-06-24 15:13:47 +00:00
|
|
|
void of_platform_depopulate(struct device *parent)
|
2014-05-15 15:55:24 +00:00
|
|
|
{
|
2014-11-19 22:35:39 +00:00
|
|
|
if (parent->of_node && of_node_check_flag(parent->of_node, OF_POPULATED_BUS)) {
|
2020-08-06 15:36:50 +00:00
|
|
|
device_for_each_child_reverse(parent, NULL, of_platform_device_destroy);
|
2014-11-19 22:35:39 +00:00
|
|
|
of_node_clear_flag(parent->of_node, OF_POPULATED_BUS);
|
|
|
|
}
|
2014-05-15 15:55:24 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(of_platform_depopulate);
|
|
|
|
|
2017-02-24 16:14:33 +00:00
|
|
|
static void devm_of_platform_populate_release(struct device *dev, void *res)
|
|
|
|
{
|
|
|
|
of_platform_depopulate(*(struct device **)res);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* devm_of_platform_populate() - Populate platform_devices from device tree data
|
|
|
|
* @dev: device that requested to populate from device tree data
|
|
|
|
*
|
|
|
|
* Similar to of_platform_populate(), but will automatically call
|
|
|
|
* of_platform_depopulate() when the device is unbound from the bus.
|
|
|
|
*
|
2021-03-25 16:47:12 +00:00
|
|
|
* Return: 0 on success, < 0 on failure.
|
2017-02-24 16:14:33 +00:00
|
|
|
*/
|
|
|
|
int devm_of_platform_populate(struct device *dev)
|
|
|
|
{
|
|
|
|
struct device **ptr;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!dev)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
ptr = devres_alloc(devm_of_platform_populate_release,
|
|
|
|
sizeof(*ptr), GFP_KERNEL);
|
|
|
|
if (!ptr)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
|
|
|
|
if (ret) {
|
|
|
|
devres_free(ptr);
|
|
|
|
} else {
|
|
|
|
*ptr = dev;
|
|
|
|
devres_add(dev, ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(devm_of_platform_populate);
|
|
|
|
|
|
|
|
static int devm_of_platform_match(struct device *dev, void *res, void *data)
|
|
|
|
{
|
|
|
|
struct device **ptr = res;
|
|
|
|
|
|
|
|
if (!ptr) {
|
|
|
|
WARN_ON(!ptr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return *ptr == data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* devm_of_platform_depopulate() - Remove devices populated from device tree
|
|
|
|
* @dev: device that requested to depopulate from device tree data
|
|
|
|
*
|
|
|
|
* Complementary to devm_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).
|
|
|
|
*/
|
|
|
|
void devm_of_platform_depopulate(struct device *dev)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = devres_release(dev, devm_of_platform_populate_release,
|
|
|
|
devm_of_platform_match, dev);
|
|
|
|
|
|
|
|
WARN_ON(ret);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(devm_of_platform_depopulate);
|
|
|
|
|
2014-10-28 20:36:01 +00:00
|
|
|
#ifdef CONFIG_OF_DYNAMIC
|
|
|
|
static int of_platform_notify(struct notifier_block *nb,
|
|
|
|
unsigned long action, void *arg)
|
|
|
|
{
|
|
|
|
struct of_reconfig_data *rd = arg;
|
|
|
|
struct platform_device *pdev_parent, *pdev;
|
|
|
|
bool children_left;
|
|
|
|
|
|
|
|
switch (of_reconfig_get_state_change(action, rd)) {
|
|
|
|
case OF_RECONFIG_CHANGE_ADD:
|
|
|
|
/* verify that the parent is a bus */
|
|
|
|
if (!of_node_check_flag(rd->dn->parent, OF_POPULATED_BUS))
|
|
|
|
return NOTIFY_OK; /* not for us */
|
|
|
|
|
2014-12-16 17:45:26 +00:00
|
|
|
/* already populated? (driver using of_populate manually) */
|
|
|
|
if (of_node_check_flag(rd->dn, OF_POPULATED))
|
|
|
|
return NOTIFY_OK;
|
|
|
|
|
treewide: Fix probing of devices in DT overlays
JIRA: https://issues.redhat.com/browse/RHEL-22420
commit 1a50d9403fb90cbe4dea0ec9fd0351d2ecbd8924
Author: Geert Uytterhoeven <geert+renesas@glider.be>
Date: Thu, 30 Mar 2023 15:26:13 +0200
When loading a DT overlay that creates a device, the device is not
probed, unless the DT overlay is unloaded and reloaded again.
After the recent refactoring to improve fw_devlink, it no longer depends
on the "compatible" property to identify which device tree nodes will
become struct devices. fw_devlink now picks up dangling consumers
(consumers pointing to descendent device tree nodes of a device that
aren't converted to child devices) when a device is successfully bound
to a driver. See __fw_devlink_pickup_dangling_consumers().
However, during DT overlay, a device's device tree node can have
sub-nodes added/removed without unbinding/rebinding the driver. This
difference in behavior between the normal device instantiation and
probing flow vs. the DT overlay flow has a bunch of implications that
are pointed out elsewhere[1]. One of them is that the fw_devlink logic
to pick up dangling consumers is never exercised.
This patch solves the fw_devlink issue by marking all DT nodes added by
DT overlays with FWNODE_FLAG_NOT_DEVICE (fwnode that won't become
device), and by clearing the flag when a struct device is actually
created for the DT node. This way, fw_devlink knows not to have
consumers waiting on these newly added DT nodes, and to propagate the
dependency to an ancestor DT node that has the corresponding struct
device.
Based on a patch by Saravana Kannan, which covered only platform and spi
devices.
[1] https://lore.kernel.org/r/CAGETcx_bkuFaLCiPrAWCPQz+w79ccDp6=9e881qmK=vx3hBMyg@mail.gmail.com
Fixes: 4a032827daa89350 ("of: property: Simplify of_link_to_phandle()")
Link: https://lore.kernel.org/r/CAGETcx_+rhHvaC_HJXGrr5_WAd2+k5f=rWYnkCZ6z5bGX-wj4w@mail.gmail.com
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Mark Brown <broonie@kernel.org>
Acked-by: Wolfram Sang <wsa@kernel.org> # for I2C
Acked-by: Shawn Guo <shawnguo@kernel.org>
Acked-by: Saravana Kannan <saravanak@google.com>
Tested-by: Ivan Bornyakov <i.bornyakov@metrotek.ru>
Link: https://lore.kernel.org/r/e1fa546682ea4c8474ff997ab6244c5e11b6f8bc.1680182615.git.geert+renesas@glider.be
Signed-off-by: Rob Herring <robh@kernel.org>
Signed-off-by: Rupinderjit Singh <rusingh@redhat.com>
2024-05-29 15:48:17 +00:00
|
|
|
/*
|
|
|
|
* Clear the flag before adding the device so that fw_devlink
|
|
|
|
* doesn't skip adding consumers to this device.
|
|
|
|
*/
|
|
|
|
rd->dn->fwnode.flags &= ~FWNODE_FLAG_NOT_DEVICE;
|
2014-10-28 20:36:01 +00:00
|
|
|
/* pdev_parent may be NULL when no bus platform device */
|
|
|
|
pdev_parent = of_find_device_by_node(rd->dn->parent);
|
|
|
|
pdev = of_platform_device_create(rd->dn, NULL,
|
|
|
|
pdev_parent ? &pdev_parent->dev : NULL);
|
2021-02-11 23:27:44 +00:00
|
|
|
platform_device_put(pdev_parent);
|
2014-10-28 20:36:01 +00:00
|
|
|
|
|
|
|
if (pdev == NULL) {
|
2017-06-01 20:50:55 +00:00
|
|
|
pr_err("%s: failed to create for '%pOF'\n",
|
|
|
|
__func__, rd->dn);
|
2014-10-28 20:36:01 +00:00
|
|
|
/* of_platform_device_create tosses the error code */
|
|
|
|
return notifier_from_errno(-EINVAL);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OF_RECONFIG_CHANGE_REMOVE:
|
2014-12-16 17:45:26 +00:00
|
|
|
|
|
|
|
/* already depopulated? */
|
|
|
|
if (!of_node_check_flag(rd->dn, OF_POPULATED))
|
|
|
|
return NOTIFY_OK;
|
|
|
|
|
2014-10-28 20:36:01 +00:00
|
|
|
/* find our device by node */
|
|
|
|
pdev = of_find_device_by_node(rd->dn);
|
|
|
|
if (pdev == NULL)
|
|
|
|
return NOTIFY_OK; /* no? not meant for us */
|
|
|
|
|
|
|
|
/* unregister takes one ref away */
|
|
|
|
of_platform_device_destroy(&pdev->dev, &children_left);
|
|
|
|
|
|
|
|
/* and put the reference of the find */
|
2021-02-11 23:27:44 +00:00
|
|
|
platform_device_put(pdev);
|
2014-10-28 20:36:01 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NOTIFY_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct notifier_block platform_of_notifier = {
|
|
|
|
.notifier_call = of_platform_notify,
|
|
|
|
};
|
|
|
|
|
|
|
|
void of_platform_register_reconfig_notifier(void)
|
|
|
|
{
|
|
|
|
WARN_ON(of_reconfig_notifier_register(&platform_of_notifier));
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_OF_DYNAMIC */
|
|
|
|
|
2012-02-24 21:58:34 +00:00
|
|
|
#endif /* CONFIG_OF_ADDRESS */
|