avionic design with actual uboot and tooling
submodule of avionic design uboot bootloader and with included tools to get you started , read readme.md and readme-tk1-loader.md
This commit is contained in:
204
u-boot/drivers/core/Kconfig
Normal file
204
u-boot/drivers/core/Kconfig
Normal file
@@ -0,0 +1,204 @@
|
||||
menu "Generic Driver Options"
|
||||
|
||||
config DM
|
||||
bool "Enable Driver Model"
|
||||
help
|
||||
This config option enables Driver Model. This brings in the core
|
||||
support, including scanning of platform data on start-up. If
|
||||
CONFIG_OF_CONTROL is enabled, the device tree will be scanned also
|
||||
when available.
|
||||
|
||||
config SPL_DM
|
||||
bool "Enable Driver Model for SPL"
|
||||
depends on DM && SPL
|
||||
help
|
||||
Enable driver model in SPL. You will need to provide a
|
||||
suitable malloc() implementation. If you are not using the
|
||||
full malloc() enabled by CONFIG_SYS_SPL_MALLOC_START,
|
||||
consider using CONFIG_SYS_MALLOC_SIMPLE. In that case you
|
||||
must provide CONFIG_SYS_MALLOC_F_LEN to set the size.
|
||||
In most cases driver model will only allocate a few uclasses
|
||||
and devices in SPL, so 1KB should be enable. See
|
||||
CONFIG_SYS_MALLOC_F_LEN for more details on how to enable it.
|
||||
|
||||
config DM_WARN
|
||||
bool "Enable warnings in driver model"
|
||||
depends on DM
|
||||
default y
|
||||
help
|
||||
The dm_warn() function can use up quite a bit of space for its
|
||||
strings. By default this is disabled for SPL builds to save space.
|
||||
This will cause dm_warn() to be compiled out - it will do nothing
|
||||
when called.
|
||||
|
||||
config DM_DEVICE_REMOVE
|
||||
bool "Support device removal"
|
||||
depends on DM
|
||||
default y
|
||||
help
|
||||
We can save some code space by dropping support for removing a
|
||||
device. This is not normally required in SPL, so by default this
|
||||
option is disabled for SPL.
|
||||
|
||||
Note that this may have undesirable results in the USB subsystem as
|
||||
it causes unplugged devices to linger around in the dm-tree, and it
|
||||
causes USB host controllers to not be stopped when booting the OS.
|
||||
|
||||
config DM_STDIO
|
||||
bool "Support stdio registration"
|
||||
depends on DM
|
||||
default y
|
||||
help
|
||||
Normally serial drivers register with stdio so that they can be used
|
||||
as normal output devices. In SPL we don't normally use stdio, so
|
||||
we can omit this feature.
|
||||
|
||||
config DM_SEQ_ALIAS
|
||||
bool "Support numbered aliases in device tree"
|
||||
depends on DM
|
||||
default y
|
||||
help
|
||||
Most boards will have a '/aliases' node containing the path to
|
||||
numbered devices (e.g. serial0 = &serial0). This feature can be
|
||||
disabled if it is not required.
|
||||
|
||||
config SPL_DM_SEQ_ALIAS
|
||||
bool "Support numbered aliases in device tree in SPL"
|
||||
depends on DM
|
||||
default n
|
||||
help
|
||||
Most boards will have a '/aliases' node containing the path to
|
||||
numbered devices (e.g. serial0 = &serial0). This feature can be
|
||||
disabled if it is not required, to save code space in SPL.
|
||||
|
||||
config REGMAP
|
||||
bool "Support register maps"
|
||||
depends on DM
|
||||
help
|
||||
Hardware peripherals tend to have one or more sets of registers
|
||||
which can be accessed to control the hardware. A register map
|
||||
models this with a simple read/write interface. It can in principle
|
||||
support any bus type (I2C, SPI) but so far this only supports
|
||||
direct memory access.
|
||||
|
||||
config SPL_REGMAP
|
||||
bool "Support register maps in SPL"
|
||||
depends on DM
|
||||
help
|
||||
Hardware peripherals tend to have one or more sets of registers
|
||||
which can be accessed to control the hardware. A register map
|
||||
models this with a simple read/write interface. It can in principle
|
||||
support any bus type (I2C, SPI) but so far this only supports
|
||||
direct memory access.
|
||||
|
||||
config SYSCON
|
||||
bool "Support system controllers"
|
||||
depends on REGMAP
|
||||
help
|
||||
Many SoCs have a number of system controllers which are dealt with
|
||||
as a group by a single driver. Some common functionality is provided
|
||||
by this uclass, including accessing registers via regmap and
|
||||
assigning a unique number to each.
|
||||
|
||||
config SPL_SYSCON
|
||||
bool "Support system controllers in SPL"
|
||||
depends on REGMAP
|
||||
help
|
||||
Many SoCs have a number of system controllers which are dealt with
|
||||
as a group by a single driver. Some common functionality is provided
|
||||
by this uclass, including accessing registers via regmap and
|
||||
assigning a unique number to each.
|
||||
|
||||
config DEVRES
|
||||
bool "Managed device resources"
|
||||
depends on DM
|
||||
help
|
||||
This option enables the Managed device resources core support.
|
||||
Device resources managed by the devres framework are automatically
|
||||
released whether initialization fails half-way or the device gets
|
||||
detached.
|
||||
|
||||
If this option is disabled, devres functions fall back to
|
||||
non-managed variants. For example, devres_alloc() to kzalloc(),
|
||||
devm_kmalloc() to kmalloc(), etc.
|
||||
|
||||
config DEBUG_DEVRES
|
||||
bool "Managed device resources debugging functions"
|
||||
depends on DEVRES
|
||||
help
|
||||
If this option is enabled, devres debug messages are printed.
|
||||
Also, a function is available to dump a list of device resources.
|
||||
Select this if you are having a problem with devres or want to
|
||||
debug resource management for a managed device.
|
||||
|
||||
If you are unsure about this, Say N here.
|
||||
|
||||
config SIMPLE_BUS
|
||||
bool "Support simple-bus driver"
|
||||
depends on DM && OF_CONTROL
|
||||
default y
|
||||
help
|
||||
Supports the 'simple-bus' driver, which is used on some systems.
|
||||
|
||||
config SPL_SIMPLE_BUS
|
||||
bool "Support simple-bus driver in SPL"
|
||||
depends on SPL_DM && SPL_OF_CONTROL
|
||||
default y
|
||||
help
|
||||
Supports the 'simple-bus' driver, which is used on some systems
|
||||
in SPL.
|
||||
|
||||
config OF_TRANSLATE
|
||||
bool "Translate addresses using fdt_translate_address"
|
||||
depends on DM && OF_CONTROL
|
||||
default y
|
||||
help
|
||||
If this option is enabled, the reg property will be translated
|
||||
using the fdt_translate_address() function. This is necessary
|
||||
on some platforms (e.g. MVEBU) using complex "ranges"
|
||||
properties in many nodes. As this translation is not handled
|
||||
correctly in the default simple_bus_translate() function.
|
||||
|
||||
If this option is not enabled, simple_bus_translate() will be
|
||||
used for the address translation. This function is faster and
|
||||
smaller in size than fdt_translate_address().
|
||||
|
||||
config SPL_OF_TRANSLATE
|
||||
bool "Translate addresses using fdt_translate_address in SPL"
|
||||
depends on SPL_DM && SPL_OF_CONTROL
|
||||
default n
|
||||
help
|
||||
If this option is enabled, the reg property will be translated
|
||||
using the fdt_translate_address() function. This is necessary
|
||||
on some platforms (e.g. MVEBU) using complex "ranges"
|
||||
properties in many nodes. As this translation is not handled
|
||||
correctly in the default simple_bus_translate() function.
|
||||
|
||||
If this option is not enabled, simple_bus_translate() will be
|
||||
used for the address translation. This function is faster and
|
||||
smaller in size than fdt_translate_address().
|
||||
|
||||
config OF_ISA_BUS
|
||||
bool
|
||||
depends on OF_TRANSLATE
|
||||
help
|
||||
Is this option is enabled then support for the ISA bus will
|
||||
be included for addresses read from DT. This is something that
|
||||
should be known to be required or not based upon the board
|
||||
being targetted, and whether or not it makes use of an ISA bus.
|
||||
|
||||
The bus is matched based upon its node name equalling "isa". The
|
||||
busses #address-cells should equal 2, with the first cell being
|
||||
used to hold flags & flag 0x1 indicating that the address range
|
||||
should be accessed using I/O port in/out accessors. The second
|
||||
cell holds the offset into ISA bus address space. The #size-cells
|
||||
property should equal 1, and of course holds the size of the
|
||||
address range used by a device.
|
||||
|
||||
If this option is not enabled then support for the ISA bus is
|
||||
not included and any such busses used in DT will be treated as
|
||||
typical simple-bus compatible busses. This will lead to
|
||||
mistranslation of device addresses, so ensure that this is
|
||||
enabled if your board does include an ISA bus.
|
||||
|
||||
endmenu
|
||||
13
u-boot/drivers/core/Makefile
Normal file
13
u-boot/drivers/core/Makefile
Normal file
@@ -0,0 +1,13 @@
|
||||
#
|
||||
# Copyright (c) 2013 Google, Inc
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
obj-y += device.o lists.o root.o uclass.o util.o
|
||||
obj-$(CONFIG_DEVRES) += devres.o
|
||||
obj-$(CONFIG_$(SPL_)DM_DEVICE_REMOVE) += device-remove.o
|
||||
obj-$(CONFIG_$(SPL_)SIMPLE_BUS) += simple-bus.o
|
||||
obj-$(CONFIG_DM) += dump.o
|
||||
obj-$(CONFIG_$(SPL_)REGMAP) += regmap.o
|
||||
obj-$(CONFIG_$(SPL_)SYSCON) += syscon-uclass.o
|
||||
209
u-boot/drivers/core/device-remove.c
Normal file
209
u-boot/drivers/core/device-remove.c
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Device manager
|
||||
*
|
||||
* Copyright (c) 2014 Google, Inc
|
||||
*
|
||||
* (C) Copyright 2012
|
||||
* Pavel Herrmann <morpheus.ibis@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
#include <dm/device.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/uclass.h>
|
||||
#include <dm/uclass-internal.h>
|
||||
#include <dm/util.h>
|
||||
|
||||
/**
|
||||
* device_chld_unbind() - Unbind all device's children from the device
|
||||
*
|
||||
* On error, the function continues to unbind all children, and reports the
|
||||
* first error.
|
||||
*
|
||||
* @dev: The device that is to be stripped of its children
|
||||
* @return 0 on success, -ve on error
|
||||
*/
|
||||
static int device_chld_unbind(struct udevice *dev)
|
||||
{
|
||||
struct udevice *pos, *n;
|
||||
int ret, saved_ret = 0;
|
||||
|
||||
assert(dev);
|
||||
|
||||
list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
|
||||
ret = device_unbind(pos);
|
||||
if (ret && !saved_ret)
|
||||
saved_ret = ret;
|
||||
}
|
||||
|
||||
return saved_ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* device_chld_remove() - Stop all device's children
|
||||
* @dev: The device whose children are to be removed
|
||||
* @return 0 on success, -ve on error
|
||||
*/
|
||||
static int device_chld_remove(struct udevice *dev)
|
||||
{
|
||||
struct udevice *pos, *n;
|
||||
int ret;
|
||||
|
||||
assert(dev);
|
||||
|
||||
list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
|
||||
ret = device_remove(pos);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_unbind(struct udevice *dev)
|
||||
{
|
||||
const struct driver *drv;
|
||||
int ret;
|
||||
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
if (dev->flags & DM_FLAG_ACTIVATED)
|
||||
return -EINVAL;
|
||||
|
||||
if (!(dev->flags & DM_FLAG_BOUND))
|
||||
return -EINVAL;
|
||||
|
||||
drv = dev->driver;
|
||||
assert(drv);
|
||||
|
||||
if (drv->unbind) {
|
||||
ret = drv->unbind(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = device_chld_unbind(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (dev->flags & DM_FLAG_ALLOC_PDATA) {
|
||||
free(dev->platdata);
|
||||
dev->platdata = NULL;
|
||||
}
|
||||
if (dev->flags & DM_FLAG_ALLOC_UCLASS_PDATA) {
|
||||
free(dev->uclass_platdata);
|
||||
dev->uclass_platdata = NULL;
|
||||
}
|
||||
if (dev->flags & DM_FLAG_ALLOC_PARENT_PDATA) {
|
||||
free(dev->parent_platdata);
|
||||
dev->parent_platdata = NULL;
|
||||
}
|
||||
ret = uclass_unbind_device(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (dev->parent)
|
||||
list_del(&dev->sibling_node);
|
||||
|
||||
devres_release_all(dev);
|
||||
|
||||
if (dev->flags & DM_NAME_ALLOCED)
|
||||
free((char *)dev->name);
|
||||
free(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* device_free() - Free memory buffers allocated by a device
|
||||
* @dev: Device that is to be started
|
||||
*/
|
||||
void device_free(struct udevice *dev)
|
||||
{
|
||||
int size;
|
||||
|
||||
if (dev->driver->priv_auto_alloc_size) {
|
||||
free(dev->priv);
|
||||
dev->priv = NULL;
|
||||
}
|
||||
size = dev->uclass->uc_drv->per_device_auto_alloc_size;
|
||||
if (size) {
|
||||
free(dev->uclass_priv);
|
||||
dev->uclass_priv = NULL;
|
||||
}
|
||||
if (dev->parent) {
|
||||
size = dev->parent->driver->per_child_auto_alloc_size;
|
||||
if (!size) {
|
||||
size = dev->parent->uclass->uc_drv->
|
||||
per_child_auto_alloc_size;
|
||||
}
|
||||
if (size) {
|
||||
free(dev->parent_priv);
|
||||
dev->parent_priv = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
devres_release_probe(dev);
|
||||
}
|
||||
|
||||
int device_remove(struct udevice *dev)
|
||||
{
|
||||
const struct driver *drv;
|
||||
int ret;
|
||||
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
if (!(dev->flags & DM_FLAG_ACTIVATED))
|
||||
return 0;
|
||||
|
||||
drv = dev->driver;
|
||||
assert(drv);
|
||||
|
||||
ret = uclass_pre_remove_device(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = device_chld_remove(dev);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (drv->remove) {
|
||||
ret = drv->remove(dev);
|
||||
if (ret)
|
||||
goto err_remove;
|
||||
}
|
||||
|
||||
if (dev->parent && dev->parent->driver->child_post_remove) {
|
||||
ret = dev->parent->driver->child_post_remove(dev);
|
||||
if (ret) {
|
||||
dm_warn("%s: Device '%s' failed child_post_remove()",
|
||||
__func__, dev->name);
|
||||
}
|
||||
}
|
||||
|
||||
device_free(dev);
|
||||
|
||||
dev->seq = -1;
|
||||
dev->flags &= ~DM_FLAG_ACTIVATED;
|
||||
|
||||
return ret;
|
||||
|
||||
err_remove:
|
||||
/* We can't put the children back */
|
||||
dm_warn("%s: Device '%s' failed to remove, but children are gone\n",
|
||||
__func__, dev->name);
|
||||
err:
|
||||
ret = uclass_post_probe_device(dev);
|
||||
if (ret) {
|
||||
dm_warn("%s: Device '%s' failed to post_probe on error path\n",
|
||||
__func__, dev->name);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
756
u-boot/drivers/core/device.c
Normal file
756
u-boot/drivers/core/device.c
Normal file
@@ -0,0 +1,756 @@
|
||||
/*
|
||||
* Device manager
|
||||
*
|
||||
* Copyright (c) 2013 Google, Inc
|
||||
*
|
||||
* (C) Copyright 2012
|
||||
* Pavel Herrmann <morpheus.ibis@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <fdtdec.h>
|
||||
#include <fdt_support.h>
|
||||
#include <malloc.h>
|
||||
#include <dm/device.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/pinctrl.h>
|
||||
#include <dm/platdata.h>
|
||||
#include <dm/uclass.h>
|
||||
#include <dm/uclass-internal.h>
|
||||
#include <dm/util.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
static int device_bind_common(struct udevice *parent, const struct driver *drv,
|
||||
const char *name, void *platdata,
|
||||
ulong driver_data, int of_offset,
|
||||
struct udevice **devp)
|
||||
{
|
||||
struct udevice *dev;
|
||||
struct uclass *uc;
|
||||
int size, ret = 0;
|
||||
|
||||
if (devp)
|
||||
*devp = NULL;
|
||||
if (!name)
|
||||
return -EINVAL;
|
||||
|
||||
ret = uclass_get(drv->id, &uc);
|
||||
if (ret) {
|
||||
debug("Missing uclass for driver %s\n", drv->name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev = calloc(1, sizeof(struct udevice));
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&dev->sibling_node);
|
||||
INIT_LIST_HEAD(&dev->child_head);
|
||||
INIT_LIST_HEAD(&dev->uclass_node);
|
||||
#ifdef CONFIG_DEVRES
|
||||
INIT_LIST_HEAD(&dev->devres_head);
|
||||
#endif
|
||||
dev->platdata = platdata;
|
||||
dev->driver_data = driver_data;
|
||||
dev->name = name;
|
||||
dev->of_offset = of_offset;
|
||||
dev->parent = parent;
|
||||
dev->driver = drv;
|
||||
dev->uclass = uc;
|
||||
|
||||
dev->seq = -1;
|
||||
dev->req_seq = -1;
|
||||
if (CONFIG_IS_ENABLED(OF_CONTROL) && CONFIG_IS_ENABLED(DM_SEQ_ALIAS)) {
|
||||
/*
|
||||
* Some devices, such as a SPI bus, I2C bus and serial ports
|
||||
* are numbered using aliases.
|
||||
*
|
||||
* This is just a 'requested' sequence, and will be
|
||||
* resolved (and ->seq updated) when the device is probed.
|
||||
*/
|
||||
if (uc->uc_drv->flags & DM_UC_FLAG_SEQ_ALIAS) {
|
||||
if (uc->uc_drv->name && of_offset != -1) {
|
||||
fdtdec_get_alias_seq(gd->fdt_blob,
|
||||
uc->uc_drv->name, of_offset,
|
||||
&dev->req_seq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!dev->platdata && drv->platdata_auto_alloc_size) {
|
||||
dev->flags |= DM_FLAG_ALLOC_PDATA;
|
||||
dev->platdata = calloc(1, drv->platdata_auto_alloc_size);
|
||||
if (!dev->platdata) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_alloc1;
|
||||
}
|
||||
}
|
||||
|
||||
size = uc->uc_drv->per_device_platdata_auto_alloc_size;
|
||||
if (size) {
|
||||
dev->flags |= DM_FLAG_ALLOC_UCLASS_PDATA;
|
||||
dev->uclass_platdata = calloc(1, size);
|
||||
if (!dev->uclass_platdata) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_alloc2;
|
||||
}
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
size = parent->driver->per_child_platdata_auto_alloc_size;
|
||||
if (!size) {
|
||||
size = parent->uclass->uc_drv->
|
||||
per_child_platdata_auto_alloc_size;
|
||||
}
|
||||
if (size) {
|
||||
dev->flags |= DM_FLAG_ALLOC_PARENT_PDATA;
|
||||
dev->parent_platdata = calloc(1, size);
|
||||
if (!dev->parent_platdata) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_alloc3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* put dev into parent's successor list */
|
||||
if (parent)
|
||||
list_add_tail(&dev->sibling_node, &parent->child_head);
|
||||
|
||||
ret = uclass_bind_device(dev);
|
||||
if (ret)
|
||||
goto fail_uclass_bind;
|
||||
|
||||
/* if we fail to bind we remove device from successors and free it */
|
||||
if (drv->bind) {
|
||||
ret = drv->bind(dev);
|
||||
if (ret)
|
||||
goto fail_bind;
|
||||
}
|
||||
if (parent && parent->driver->child_post_bind) {
|
||||
ret = parent->driver->child_post_bind(dev);
|
||||
if (ret)
|
||||
goto fail_child_post_bind;
|
||||
}
|
||||
if (uc->uc_drv->post_bind) {
|
||||
ret = uc->uc_drv->post_bind(dev);
|
||||
if (ret)
|
||||
goto fail_uclass_post_bind;
|
||||
}
|
||||
|
||||
if (parent)
|
||||
dm_dbg("Bound device %s to %s\n", dev->name, parent->name);
|
||||
if (devp)
|
||||
*devp = dev;
|
||||
|
||||
dev->flags |= DM_FLAG_BOUND;
|
||||
|
||||
return 0;
|
||||
|
||||
fail_uclass_post_bind:
|
||||
/* There is no child unbind() method, so no clean-up required */
|
||||
fail_child_post_bind:
|
||||
if (CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)) {
|
||||
if (drv->unbind && drv->unbind(dev)) {
|
||||
dm_warn("unbind() method failed on dev '%s' on error path\n",
|
||||
dev->name);
|
||||
}
|
||||
}
|
||||
|
||||
fail_bind:
|
||||
if (CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)) {
|
||||
if (uclass_unbind_device(dev)) {
|
||||
dm_warn("Failed to unbind dev '%s' on error path\n",
|
||||
dev->name);
|
||||
}
|
||||
}
|
||||
fail_uclass_bind:
|
||||
if (CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)) {
|
||||
list_del(&dev->sibling_node);
|
||||
if (dev->flags & DM_FLAG_ALLOC_PARENT_PDATA) {
|
||||
free(dev->parent_platdata);
|
||||
dev->parent_platdata = NULL;
|
||||
}
|
||||
}
|
||||
fail_alloc3:
|
||||
if (dev->flags & DM_FLAG_ALLOC_UCLASS_PDATA) {
|
||||
free(dev->uclass_platdata);
|
||||
dev->uclass_platdata = NULL;
|
||||
}
|
||||
fail_alloc2:
|
||||
if (dev->flags & DM_FLAG_ALLOC_PDATA) {
|
||||
free(dev->platdata);
|
||||
dev->platdata = NULL;
|
||||
}
|
||||
fail_alloc1:
|
||||
devres_release_all(dev);
|
||||
|
||||
free(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int device_bind_with_driver_data(struct udevice *parent,
|
||||
const struct driver *drv, const char *name,
|
||||
ulong driver_data, int of_offset,
|
||||
struct udevice **devp)
|
||||
{
|
||||
return device_bind_common(parent, drv, name, NULL, driver_data,
|
||||
of_offset, devp);
|
||||
}
|
||||
|
||||
int device_bind(struct udevice *parent, const struct driver *drv,
|
||||
const char *name, void *platdata, int of_offset,
|
||||
struct udevice **devp)
|
||||
{
|
||||
return device_bind_common(parent, drv, name, platdata, 0, of_offset,
|
||||
devp);
|
||||
}
|
||||
|
||||
int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
|
||||
const struct driver_info *info, struct udevice **devp)
|
||||
{
|
||||
struct driver *drv;
|
||||
|
||||
drv = lists_driver_lookup_name(info->name);
|
||||
if (!drv)
|
||||
return -ENOENT;
|
||||
if (pre_reloc_only && !(drv->flags & DM_FLAG_PRE_RELOC))
|
||||
return -EPERM;
|
||||
|
||||
return device_bind(parent, drv, info->name, (void *)info->platdata,
|
||||
-1, devp);
|
||||
}
|
||||
|
||||
static void *alloc_priv(int size, uint flags)
|
||||
{
|
||||
void *priv;
|
||||
|
||||
if (flags & DM_FLAG_ALLOC_PRIV_DMA) {
|
||||
priv = memalign(ARCH_DMA_MINALIGN, size);
|
||||
if (priv)
|
||||
memset(priv, '\0', size);
|
||||
} else {
|
||||
priv = calloc(1, size);
|
||||
}
|
||||
|
||||
return priv;
|
||||
}
|
||||
|
||||
int device_probe(struct udevice *dev)
|
||||
{
|
||||
const struct driver *drv;
|
||||
int size = 0;
|
||||
int ret;
|
||||
int seq;
|
||||
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
if (dev->flags & DM_FLAG_ACTIVATED)
|
||||
return 0;
|
||||
|
||||
drv = dev->driver;
|
||||
assert(drv);
|
||||
|
||||
/* Allocate private data if requested and not reentered */
|
||||
if (drv->priv_auto_alloc_size && !dev->priv) {
|
||||
dev->priv = alloc_priv(drv->priv_auto_alloc_size, drv->flags);
|
||||
if (!dev->priv) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
/* Allocate private data if requested and not reentered */
|
||||
size = dev->uclass->uc_drv->per_device_auto_alloc_size;
|
||||
if (size && !dev->uclass_priv) {
|
||||
dev->uclass_priv = calloc(1, size);
|
||||
if (!dev->uclass_priv) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure all parents are probed */
|
||||
if (dev->parent) {
|
||||
size = dev->parent->driver->per_child_auto_alloc_size;
|
||||
if (!size) {
|
||||
size = dev->parent->uclass->uc_drv->
|
||||
per_child_auto_alloc_size;
|
||||
}
|
||||
if (size && !dev->parent_priv) {
|
||||
dev->parent_priv = alloc_priv(size, drv->flags);
|
||||
if (!dev->parent_priv) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
ret = device_probe(dev->parent);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* The device might have already been probed during
|
||||
* the call to device_probe() on its parent device
|
||||
* (e.g. PCI bridge devices). Test the flags again
|
||||
* so that we don't mess up the device.
|
||||
*/
|
||||
if (dev->flags & DM_FLAG_ACTIVATED)
|
||||
return 0;
|
||||
}
|
||||
|
||||
seq = uclass_resolve_seq(dev);
|
||||
if (seq < 0) {
|
||||
ret = seq;
|
||||
goto fail;
|
||||
}
|
||||
dev->seq = seq;
|
||||
|
||||
dev->flags |= DM_FLAG_ACTIVATED;
|
||||
|
||||
/*
|
||||
* Process pinctrl for everything except the root device, and
|
||||
* continue regardless of the result of pinctrl. Don't process pinctrl
|
||||
* settings for pinctrl devices since the device may not yet be
|
||||
* probed.
|
||||
*/
|
||||
if (dev->parent && device_get_uclass_id(dev) != UCLASS_PINCTRL)
|
||||
pinctrl_select_state(dev, "default");
|
||||
|
||||
ret = uclass_pre_probe_device(dev);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
if (dev->parent && dev->parent->driver->child_pre_probe) {
|
||||
ret = dev->parent->driver->child_pre_probe(dev);
|
||||
if (ret)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (drv->ofdata_to_platdata && dev->of_offset >= 0) {
|
||||
ret = drv->ofdata_to_platdata(dev);
|
||||
if (ret)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (drv->probe) {
|
||||
ret = drv->probe(dev);
|
||||
if (ret) {
|
||||
dev->flags &= ~DM_FLAG_ACTIVATED;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
ret = uclass_post_probe_device(dev);
|
||||
if (ret)
|
||||
goto fail_uclass;
|
||||
|
||||
if (dev->parent && device_get_uclass_id(dev) == UCLASS_PINCTRL)
|
||||
pinctrl_select_state(dev, "default");
|
||||
|
||||
return 0;
|
||||
fail_uclass:
|
||||
if (device_remove(dev)) {
|
||||
dm_warn("%s: Device '%s' failed to remove on error path\n",
|
||||
__func__, dev->name);
|
||||
}
|
||||
fail:
|
||||
dev->flags &= ~DM_FLAG_ACTIVATED;
|
||||
|
||||
dev->seq = -1;
|
||||
device_free(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *dev_get_platdata(struct udevice *dev)
|
||||
{
|
||||
if (!dev) {
|
||||
dm_warn("%s: null device\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dev->platdata;
|
||||
}
|
||||
|
||||
void *dev_get_parent_platdata(struct udevice *dev)
|
||||
{
|
||||
if (!dev) {
|
||||
dm_warn("%s: null device\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dev->parent_platdata;
|
||||
}
|
||||
|
||||
void *dev_get_uclass_platdata(struct udevice *dev)
|
||||
{
|
||||
if (!dev) {
|
||||
dm_warn("%s: null device\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dev->uclass_platdata;
|
||||
}
|
||||
|
||||
void *dev_get_priv(struct udevice *dev)
|
||||
{
|
||||
if (!dev) {
|
||||
dm_warn("%s: null device\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dev->priv;
|
||||
}
|
||||
|
||||
void *dev_get_uclass_priv(struct udevice *dev)
|
||||
{
|
||||
if (!dev) {
|
||||
dm_warn("%s: null device\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dev->uclass_priv;
|
||||
}
|
||||
|
||||
void *dev_get_parent_priv(struct udevice *dev)
|
||||
{
|
||||
if (!dev) {
|
||||
dm_warn("%s: null device\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dev->parent_priv;
|
||||
}
|
||||
|
||||
static int device_get_device_tail(struct udevice *dev, int ret,
|
||||
struct udevice **devp)
|
||||
{
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = device_probe(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*devp = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_get_child(struct udevice *parent, int index, struct udevice **devp)
|
||||
{
|
||||
struct udevice *dev;
|
||||
|
||||
list_for_each_entry(dev, &parent->child_head, sibling_node) {
|
||||
if (!index--)
|
||||
return device_get_device_tail(dev, 0, devp);
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int device_find_child_by_seq(struct udevice *parent, int seq_or_req_seq,
|
||||
bool find_req_seq, struct udevice **devp)
|
||||
{
|
||||
struct udevice *dev;
|
||||
|
||||
*devp = NULL;
|
||||
if (seq_or_req_seq == -1)
|
||||
return -ENODEV;
|
||||
|
||||
list_for_each_entry(dev, &parent->child_head, sibling_node) {
|
||||
if ((find_req_seq ? dev->req_seq : dev->seq) ==
|
||||
seq_or_req_seq) {
|
||||
*devp = dev;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int device_get_child_by_seq(struct udevice *parent, int seq,
|
||||
struct udevice **devp)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
*devp = NULL;
|
||||
ret = device_find_child_by_seq(parent, seq, false, &dev);
|
||||
if (ret == -ENODEV) {
|
||||
/*
|
||||
* We didn't find it in probed devices. See if there is one
|
||||
* that will request this seq if probed.
|
||||
*/
|
||||
ret = device_find_child_by_seq(parent, seq, true, &dev);
|
||||
}
|
||||
return device_get_device_tail(dev, ret, devp);
|
||||
}
|
||||
|
||||
int device_find_child_by_of_offset(struct udevice *parent, int of_offset,
|
||||
struct udevice **devp)
|
||||
{
|
||||
struct udevice *dev;
|
||||
|
||||
*devp = NULL;
|
||||
|
||||
list_for_each_entry(dev, &parent->child_head, sibling_node) {
|
||||
if (dev->of_offset == of_offset) {
|
||||
*devp = dev;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int device_get_child_by_of_offset(struct udevice *parent, int node,
|
||||
struct udevice **devp)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
*devp = NULL;
|
||||
ret = device_find_child_by_of_offset(parent, node, &dev);
|
||||
return device_get_device_tail(dev, ret, devp);
|
||||
}
|
||||
|
||||
static struct udevice *_device_find_global_by_of_offset(struct udevice *parent,
|
||||
int of_offset)
|
||||
{
|
||||
struct udevice *dev, *found;
|
||||
|
||||
if (parent->of_offset == of_offset)
|
||||
return parent;
|
||||
|
||||
list_for_each_entry(dev, &parent->child_head, sibling_node) {
|
||||
found = _device_find_global_by_of_offset(dev, of_offset);
|
||||
if (found)
|
||||
return found;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int device_get_global_by_of_offset(int of_offset, struct udevice **devp)
|
||||
{
|
||||
struct udevice *dev;
|
||||
|
||||
dev = _device_find_global_by_of_offset(gd->dm_root, of_offset);
|
||||
return device_get_device_tail(dev, dev ? 0 : -ENOENT, devp);
|
||||
}
|
||||
|
||||
int device_find_first_child(struct udevice *parent, struct udevice **devp)
|
||||
{
|
||||
if (list_empty(&parent->child_head)) {
|
||||
*devp = NULL;
|
||||
} else {
|
||||
*devp = list_first_entry(&parent->child_head, struct udevice,
|
||||
sibling_node);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_find_next_child(struct udevice **devp)
|
||||
{
|
||||
struct udevice *dev = *devp;
|
||||
struct udevice *parent = dev->parent;
|
||||
|
||||
if (list_is_last(&dev->sibling_node, &parent->child_head)) {
|
||||
*devp = NULL;
|
||||
} else {
|
||||
*devp = list_entry(dev->sibling_node.next, struct udevice,
|
||||
sibling_node);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct udevice *dev_get_parent(struct udevice *child)
|
||||
{
|
||||
return child->parent;
|
||||
}
|
||||
|
||||
ulong dev_get_driver_data(struct udevice *dev)
|
||||
{
|
||||
return dev->driver_data;
|
||||
}
|
||||
|
||||
const void *dev_get_driver_ops(struct udevice *dev)
|
||||
{
|
||||
if (!dev || !dev->driver->ops)
|
||||
return NULL;
|
||||
|
||||
return dev->driver->ops;
|
||||
}
|
||||
|
||||
enum uclass_id device_get_uclass_id(struct udevice *dev)
|
||||
{
|
||||
return dev->uclass->uc_drv->id;
|
||||
}
|
||||
|
||||
const char *dev_get_uclass_name(struct udevice *dev)
|
||||
{
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
return dev->uclass->uc_drv->name;
|
||||
}
|
||||
|
||||
fdt_addr_t dev_get_addr_index(struct udevice *dev, int index)
|
||||
{
|
||||
#if CONFIG_IS_ENABLED(OF_CONTROL)
|
||||
fdt_addr_t addr;
|
||||
|
||||
if (CONFIG_IS_ENABLED(OF_TRANSLATE)) {
|
||||
const fdt32_t *reg;
|
||||
int len = 0;
|
||||
int na, ns;
|
||||
|
||||
na = fdt_address_cells(gd->fdt_blob, dev->parent->of_offset);
|
||||
if (na < 1) {
|
||||
debug("bad #address-cells\n");
|
||||
return FDT_ADDR_T_NONE;
|
||||
}
|
||||
|
||||
ns = fdt_size_cells(gd->fdt_blob, dev->parent->of_offset);
|
||||
if (ns < 0) {
|
||||
debug("bad #size-cells\n");
|
||||
return FDT_ADDR_T_NONE;
|
||||
}
|
||||
|
||||
reg = fdt_getprop(gd->fdt_blob, dev->of_offset, "reg", &len);
|
||||
if (!reg || (len <= (index * sizeof(fdt32_t) * (na + ns)))) {
|
||||
debug("Req index out of range\n");
|
||||
return FDT_ADDR_T_NONE;
|
||||
}
|
||||
|
||||
reg += index * (na + ns);
|
||||
|
||||
/*
|
||||
* Use the full-fledged translate function for complex
|
||||
* bus setups.
|
||||
*/
|
||||
addr = fdt_translate_address((void *)gd->fdt_blob,
|
||||
dev->of_offset, reg);
|
||||
} else {
|
||||
/*
|
||||
* Use the "simple" translate function for less complex
|
||||
* bus setups.
|
||||
*/
|
||||
addr = fdtdec_get_addr_size_auto_parent(gd->fdt_blob,
|
||||
dev->parent->of_offset,
|
||||
dev->of_offset, "reg",
|
||||
index, NULL);
|
||||
if (CONFIG_IS_ENABLED(SIMPLE_BUS) && addr != FDT_ADDR_T_NONE) {
|
||||
if (device_get_uclass_id(dev->parent) ==
|
||||
UCLASS_SIMPLE_BUS)
|
||||
addr = simple_bus_translate(dev->parent, addr);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Some platforms need a special address translation. Those
|
||||
* platforms (e.g. mvebu in SPL) can configure a translation
|
||||
* offset in the DM by calling dm_set_translation_offset() that
|
||||
* will get added to all addresses returned by dev_get_addr().
|
||||
*/
|
||||
addr += dm_get_translation_offset();
|
||||
|
||||
return addr;
|
||||
#else
|
||||
return FDT_ADDR_T_NONE;
|
||||
#endif
|
||||
}
|
||||
|
||||
fdt_addr_t dev_get_addr_name(struct udevice *dev, const char *name)
|
||||
{
|
||||
#if CONFIG_IS_ENABLED(OF_CONTROL)
|
||||
int index;
|
||||
|
||||
index = fdt_find_string(gd->fdt_blob, dev->of_offset, "reg-names",
|
||||
name);
|
||||
if (index < 0)
|
||||
return index;
|
||||
|
||||
return dev_get_addr_index(dev, index);
|
||||
#else
|
||||
return FDT_ADDR_T_NONE;
|
||||
#endif
|
||||
}
|
||||
|
||||
fdt_addr_t dev_get_addr(struct udevice *dev)
|
||||
{
|
||||
return dev_get_addr_index(dev, 0);
|
||||
}
|
||||
|
||||
void *dev_get_addr_ptr(struct udevice *dev)
|
||||
{
|
||||
return (void *)(uintptr_t)dev_get_addr_index(dev, 0);
|
||||
}
|
||||
|
||||
bool device_has_children(struct udevice *dev)
|
||||
{
|
||||
return !list_empty(&dev->child_head);
|
||||
}
|
||||
|
||||
bool device_has_active_children(struct udevice *dev)
|
||||
{
|
||||
struct udevice *child;
|
||||
|
||||
for (device_find_first_child(dev, &child);
|
||||
child;
|
||||
device_find_next_child(&child)) {
|
||||
if (device_active(child))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool device_is_last_sibling(struct udevice *dev)
|
||||
{
|
||||
struct udevice *parent = dev->parent;
|
||||
|
||||
if (!parent)
|
||||
return false;
|
||||
return list_is_last(&dev->sibling_node, &parent->child_head);
|
||||
}
|
||||
|
||||
void device_set_name_alloced(struct udevice *dev)
|
||||
{
|
||||
dev->flags |= DM_NAME_ALLOCED;
|
||||
}
|
||||
|
||||
int device_set_name(struct udevice *dev, const char *name)
|
||||
{
|
||||
name = strdup(name);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
dev->name = name;
|
||||
device_set_name_alloced(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool of_device_is_compatible(struct udevice *dev, const char *compat)
|
||||
{
|
||||
const void *fdt = gd->fdt_blob;
|
||||
|
||||
return !fdt_node_check_compatible(fdt, dev->of_offset, compat);
|
||||
}
|
||||
|
||||
bool of_machine_is_compatible(const char *compat)
|
||||
{
|
||||
const void *fdt = gd->fdt_blob;
|
||||
|
||||
return !fdt_node_check_compatible(fdt, 0, compat);
|
||||
}
|
||||
259
u-boot/drivers/core/devres.c
Normal file
259
u-boot/drivers/core/devres.c
Normal file
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||
*
|
||||
* Based on the original work in Linux by
|
||||
* Copyright (c) 2006 SUSE Linux Products GmbH
|
||||
* Copyright (c) 2006 Tejun Heo <teheo@suse.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <dm/device.h>
|
||||
#include <dm/root.h>
|
||||
#include <dm/util.h>
|
||||
|
||||
/**
|
||||
* struct devres - Bookkeeping info for managed device resource
|
||||
* @entry: List to associate this structure with a device
|
||||
* @release: Callback invoked when this resource is released
|
||||
* @probe: Flag to show when this resource was allocated
|
||||
(true = probe, false = bind)
|
||||
* @name: Name of release function
|
||||
* @size: Size of resource data
|
||||
* @data: Resource data
|
||||
*/
|
||||
struct devres {
|
||||
struct list_head entry;
|
||||
dr_release_t release;
|
||||
bool probe;
|
||||
#ifdef CONFIG_DEBUG_DEVRES
|
||||
const char *name;
|
||||
size_t size;
|
||||
#endif
|
||||
unsigned long long data[];
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEBUG_DEVRES
|
||||
static void set_node_dbginfo(struct devres *dr, const char *name, size_t size)
|
||||
{
|
||||
dr->name = name;
|
||||
dr->size = size;
|
||||
}
|
||||
|
||||
static void devres_log(struct udevice *dev, struct devres *dr,
|
||||
const char *op)
|
||||
{
|
||||
printf("%s: DEVRES %3s %p %s (%lu bytes)\n",
|
||||
dev->name, op, dr, dr->name, (unsigned long)dr->size);
|
||||
}
|
||||
#else /* CONFIG_DEBUG_DEVRES */
|
||||
#define set_node_dbginfo(dr, n, s) do {} while (0)
|
||||
#define devres_log(dev, dr, op) do {} while (0)
|
||||
#endif
|
||||
|
||||
#if CONFIG_DEBUG_DEVRES
|
||||
void *__devres_alloc(dr_release_t release, size_t size, gfp_t gfp,
|
||||
const char *name)
|
||||
#else
|
||||
void *_devres_alloc(dr_release_t release, size_t size, gfp_t gfp)
|
||||
#endif
|
||||
{
|
||||
size_t tot_size = sizeof(struct devres) + size;
|
||||
struct devres *dr;
|
||||
|
||||
dr = kmalloc(tot_size, gfp);
|
||||
if (unlikely(!dr))
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&dr->entry);
|
||||
dr->release = release;
|
||||
set_node_dbginfo(dr, name, size);
|
||||
|
||||
return dr->data;
|
||||
}
|
||||
|
||||
void devres_free(void *res)
|
||||
{
|
||||
if (res) {
|
||||
struct devres *dr = container_of(res, struct devres, data);
|
||||
|
||||
BUG_ON(!list_empty(&dr->entry));
|
||||
kfree(dr);
|
||||
}
|
||||
}
|
||||
|
||||
void devres_add(struct udevice *dev, void *res)
|
||||
{
|
||||
struct devres *dr = container_of(res, struct devres, data);
|
||||
|
||||
devres_log(dev, dr, "ADD");
|
||||
BUG_ON(!list_empty(&dr->entry));
|
||||
dr->probe = dev->flags & DM_FLAG_BOUND ? true : false;
|
||||
list_add_tail(&dr->entry, &dev->devres_head);
|
||||
}
|
||||
|
||||
void *devres_find(struct udevice *dev, dr_release_t release,
|
||||
dr_match_t match, void *match_data)
|
||||
{
|
||||
struct devres *dr;
|
||||
|
||||
list_for_each_entry_reverse(dr, &dev->devres_head, entry) {
|
||||
if (dr->release != release)
|
||||
continue;
|
||||
if (match && !match(dev, dr->data, match_data))
|
||||
continue;
|
||||
return dr->data;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *devres_get(struct udevice *dev, void *new_res,
|
||||
dr_match_t match, void *match_data)
|
||||
{
|
||||
struct devres *new_dr = container_of(new_res, struct devres, data);
|
||||
void *res;
|
||||
|
||||
res = devres_find(dev, new_dr->release, match, match_data);
|
||||
if (!res) {
|
||||
devres_add(dev, new_res);
|
||||
res = new_res;
|
||||
new_res = NULL;
|
||||
}
|
||||
devres_free(new_res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void *devres_remove(struct udevice *dev, dr_release_t release,
|
||||
dr_match_t match, void *match_data)
|
||||
{
|
||||
void *res;
|
||||
|
||||
res = devres_find(dev, release, match, match_data);
|
||||
if (res) {
|
||||
struct devres *dr = container_of(res, struct devres, data);
|
||||
|
||||
list_del_init(&dr->entry);
|
||||
devres_log(dev, dr, "REM");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int devres_destroy(struct udevice *dev, dr_release_t release,
|
||||
dr_match_t match, void *match_data)
|
||||
{
|
||||
void *res;
|
||||
|
||||
res = devres_remove(dev, release, match, match_data);
|
||||
if (unlikely(!res))
|
||||
return -ENOENT;
|
||||
|
||||
devres_free(res);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int devres_release(struct udevice *dev, dr_release_t release,
|
||||
dr_match_t match, void *match_data)
|
||||
{
|
||||
void *res;
|
||||
|
||||
res = devres_remove(dev, release, match, match_data);
|
||||
if (unlikely(!res))
|
||||
return -ENOENT;
|
||||
|
||||
(*release)(dev, res);
|
||||
devres_free(res);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void release_nodes(struct udevice *dev, struct list_head *head,
|
||||
bool probe_only)
|
||||
{
|
||||
struct devres *dr, *tmp;
|
||||
|
||||
list_for_each_entry_safe_reverse(dr, tmp, head, entry) {
|
||||
if (probe_only && !dr->probe)
|
||||
break;
|
||||
devres_log(dev, dr, "REL");
|
||||
dr->release(dev, dr->data);
|
||||
list_del(&dr->entry);
|
||||
kfree(dr);
|
||||
}
|
||||
}
|
||||
|
||||
void devres_release_probe(struct udevice *dev)
|
||||
{
|
||||
release_nodes(dev, &dev->devres_head, true);
|
||||
}
|
||||
|
||||
void devres_release_all(struct udevice *dev)
|
||||
{
|
||||
release_nodes(dev, &dev->devres_head, false);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_DEVRES
|
||||
static void dump_resources(struct udevice *dev, int depth)
|
||||
{
|
||||
struct devres *dr;
|
||||
struct udevice *child;
|
||||
|
||||
printf("- %s\n", dev->name);
|
||||
|
||||
list_for_each_entry(dr, &dev->devres_head, entry)
|
||||
printf(" %p (%lu byte) %s %s\n", dr,
|
||||
(unsigned long)dr->size, dr->name,
|
||||
dr->probe ? "PROBE" : "BIND");
|
||||
|
||||
list_for_each_entry(child, &dev->child_head, sibling_node)
|
||||
dump_resources(child, depth + 1);
|
||||
}
|
||||
|
||||
void dm_dump_devres(void)
|
||||
{
|
||||
struct udevice *root;
|
||||
|
||||
root = dm_root();
|
||||
if (root)
|
||||
dump_resources(root, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Managed kmalloc/kfree
|
||||
*/
|
||||
static void devm_kmalloc_release(struct udevice *dev, void *res)
|
||||
{
|
||||
/* noop */
|
||||
}
|
||||
|
||||
static int devm_kmalloc_match(struct udevice *dev, void *res, void *data)
|
||||
{
|
||||
return res == data;
|
||||
}
|
||||
|
||||
void *devm_kmalloc(struct udevice *dev, size_t size, gfp_t gfp)
|
||||
{
|
||||
void *data;
|
||||
|
||||
data = _devres_alloc(devm_kmalloc_release, size, gfp);
|
||||
if (unlikely(!data))
|
||||
return NULL;
|
||||
|
||||
devres_add(dev, data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void devm_kfree(struct udevice *dev, void *p)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = devres_destroy(dev, devm_kmalloc_release, devm_kmalloc_match, p);
|
||||
WARN_ON(rc);
|
||||
}
|
||||
96
u-boot/drivers/core/dump.c
Normal file
96
u-boot/drivers/core/dump.c
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <mapmem.h>
|
||||
#include <dm/root.h>
|
||||
|
||||
static void show_devices(struct udevice *dev, int depth, int last_flag)
|
||||
{
|
||||
int i, is_last;
|
||||
struct udevice *child;
|
||||
char class_name[12];
|
||||
|
||||
/* print the first 11 characters to not break the tree-format. */
|
||||
strlcpy(class_name, dev->uclass->uc_drv->name, sizeof(class_name));
|
||||
printf(" %-11s [ %c ] ", class_name,
|
||||
dev->flags & DM_FLAG_ACTIVATED ? '+' : ' ');
|
||||
|
||||
for (i = depth; i >= 0; i--) {
|
||||
is_last = (last_flag >> i) & 1;
|
||||
if (i) {
|
||||
if (is_last)
|
||||
printf(" ");
|
||||
else
|
||||
printf("| ");
|
||||
} else {
|
||||
if (is_last)
|
||||
printf("`-- ");
|
||||
else
|
||||
printf("|-- ");
|
||||
}
|
||||
}
|
||||
|
||||
printf("%s\n", dev->name);
|
||||
|
||||
list_for_each_entry(child, &dev->child_head, sibling_node) {
|
||||
is_last = list_is_last(&child->sibling_node, &dev->child_head);
|
||||
show_devices(child, depth + 1, (last_flag << 1) | is_last);
|
||||
}
|
||||
}
|
||||
|
||||
void dm_dump_all(void)
|
||||
{
|
||||
struct udevice *root;
|
||||
|
||||
root = dm_root();
|
||||
if (root) {
|
||||
printf(" Class Probed Name\n");
|
||||
printf("----------------------------------------\n");
|
||||
show_devices(root, -1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_display_line() - Display information about a single device
|
||||
*
|
||||
* Displays a single line of information with an option prefix
|
||||
*
|
||||
* @dev: Device to display
|
||||
*/
|
||||
static void dm_display_line(struct udevice *dev)
|
||||
{
|
||||
printf("- %c %s @ %08lx",
|
||||
dev->flags & DM_FLAG_ACTIVATED ? '*' : ' ',
|
||||
dev->name, (ulong)map_to_sysmem(dev));
|
||||
if (dev->seq != -1 || dev->req_seq != -1)
|
||||
printf(", seq %d, (req %d)", dev->seq, dev->req_seq);
|
||||
puts("\n");
|
||||
}
|
||||
|
||||
void dm_dump_uclass(void)
|
||||
{
|
||||
struct uclass *uc;
|
||||
int ret;
|
||||
int id;
|
||||
|
||||
for (id = 0; id < UCLASS_COUNT; id++) {
|
||||
struct udevice *dev;
|
||||
|
||||
ret = uclass_get(id, &uc);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
printf("uclass %d: %s\n", id, uc->uc_drv->name);
|
||||
if (list_empty(&uc->dev_head))
|
||||
continue;
|
||||
list_for_each_entry(dev, &uc->dev_head, uclass_node) {
|
||||
dm_display_line(dev);
|
||||
}
|
||||
puts("\n");
|
||||
}
|
||||
}
|
||||
198
u-boot/drivers/core/lists.c
Normal file
198
u-boot/drivers/core/lists.c
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Google, Inc
|
||||
*
|
||||
* (C) Copyright 2012
|
||||
* Marek Vasut <marex@denx.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <errno.h>
|
||||
#include <dm/device.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/platdata.h>
|
||||
#include <dm/uclass.h>
|
||||
#include <dm/util.h>
|
||||
#include <fdtdec.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
struct driver *lists_driver_lookup_name(const char *name)
|
||||
{
|
||||
struct driver *drv =
|
||||
ll_entry_start(struct driver, driver);
|
||||
const int n_ents = ll_entry_count(struct driver, driver);
|
||||
struct driver *entry;
|
||||
|
||||
for (entry = drv; entry != drv + n_ents; entry++) {
|
||||
if (!strcmp(name, entry->name))
|
||||
return entry;
|
||||
}
|
||||
|
||||
/* Not found */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct uclass_driver *lists_uclass_lookup(enum uclass_id id)
|
||||
{
|
||||
struct uclass_driver *uclass =
|
||||
ll_entry_start(struct uclass_driver, uclass);
|
||||
const int n_ents = ll_entry_count(struct uclass_driver, uclass);
|
||||
struct uclass_driver *entry;
|
||||
|
||||
for (entry = uclass; entry != uclass + n_ents; entry++) {
|
||||
if (entry->id == id)
|
||||
return entry;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only)
|
||||
{
|
||||
struct driver_info *info =
|
||||
ll_entry_start(struct driver_info, driver_info);
|
||||
const int n_ents = ll_entry_count(struct driver_info, driver_info);
|
||||
struct driver_info *entry;
|
||||
struct udevice *dev;
|
||||
int result = 0;
|
||||
int ret;
|
||||
|
||||
for (entry = info; entry != info + n_ents; entry++) {
|
||||
ret = device_bind_by_name(parent, pre_reloc_only, entry, &dev);
|
||||
if (ret && ret != -EPERM) {
|
||||
dm_warn("No match for driver '%s'\n", entry->name);
|
||||
if (!result || ret != -ENOENT)
|
||||
result = ret;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int device_bind_driver(struct udevice *parent, const char *drv_name,
|
||||
const char *dev_name, struct udevice **devp)
|
||||
{
|
||||
return device_bind_driver_to_node(parent, drv_name, dev_name, -1, devp);
|
||||
}
|
||||
|
||||
int device_bind_driver_to_node(struct udevice *parent, const char *drv_name,
|
||||
const char *dev_name, int node,
|
||||
struct udevice **devp)
|
||||
{
|
||||
struct driver *drv;
|
||||
int ret;
|
||||
|
||||
drv = lists_driver_lookup_name(drv_name);
|
||||
if (!drv) {
|
||||
debug("Cannot find driver '%s'\n", drv_name);
|
||||
return -ENOENT;
|
||||
}
|
||||
ret = device_bind(parent, drv, dev_name, NULL, node, devp);
|
||||
if (ret) {
|
||||
debug("Cannot create device named '%s' (err=%d)\n",
|
||||
dev_name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(OF_CONTROL)
|
||||
/**
|
||||
* driver_check_compatible() - Check if a driver is compatible with this node
|
||||
*
|
||||
* @param blob: Device tree pointer
|
||||
* @param offset: Offset of node in device tree
|
||||
* @param of_match: List of compatible strings to match
|
||||
* @param of_idp: Returns the match that was found
|
||||
* @return 0 if there is a match, -ENOENT if no match, -ENODEV if the node
|
||||
* does not have a compatible string, other error <0 if there is a device
|
||||
* tree error
|
||||
*/
|
||||
static int driver_check_compatible(const void *blob, int offset,
|
||||
const struct udevice_id *of_match,
|
||||
const struct udevice_id **of_idp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
*of_idp = NULL;
|
||||
if (!of_match)
|
||||
return -ENOENT;
|
||||
|
||||
while (of_match->compatible) {
|
||||
ret = fdt_node_check_compatible(blob, offset,
|
||||
of_match->compatible);
|
||||
if (!ret) {
|
||||
*of_idp = of_match;
|
||||
return 0;
|
||||
} else if (ret == -FDT_ERR_NOTFOUND) {
|
||||
return -ENODEV;
|
||||
} else if (ret < 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
of_match++;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int lists_bind_fdt(struct udevice *parent, const void *blob, int offset,
|
||||
struct udevice **devp)
|
||||
{
|
||||
struct driver *driver = ll_entry_start(struct driver, driver);
|
||||
const int n_ents = ll_entry_count(struct driver, driver);
|
||||
const struct udevice_id *id;
|
||||
struct driver *entry;
|
||||
struct udevice *dev;
|
||||
bool found = false;
|
||||
const char *name;
|
||||
int result = 0;
|
||||
int ret = 0;
|
||||
|
||||
dm_dbg("bind node %s\n", fdt_get_name(blob, offset, NULL));
|
||||
if (devp)
|
||||
*devp = NULL;
|
||||
for (entry = driver; entry != driver + n_ents; entry++) {
|
||||
ret = driver_check_compatible(blob, offset, entry->of_match,
|
||||
&id);
|
||||
name = fdt_get_name(blob, offset, NULL);
|
||||
if (ret == -ENOENT) {
|
||||
continue;
|
||||
} else if (ret == -ENODEV) {
|
||||
dm_dbg("Device '%s' has no compatible string\n", name);
|
||||
break;
|
||||
} else if (ret) {
|
||||
dm_warn("Device tree error at offset %d\n", offset);
|
||||
result = ret;
|
||||
break;
|
||||
}
|
||||
|
||||
dm_dbg(" - found match at '%s'\n", entry->name);
|
||||
ret = device_bind_with_driver_data(parent, entry, name,
|
||||
id->data, offset, &dev);
|
||||
if (ret == -ENODEV) {
|
||||
dm_dbg("Driver '%s' refuses to bind\n", entry->name);
|
||||
continue;
|
||||
}
|
||||
if (ret) {
|
||||
dm_warn("Error binding driver '%s': %d\n", entry->name,
|
||||
ret);
|
||||
return ret;
|
||||
} else {
|
||||
found = true;
|
||||
if (devp)
|
||||
*devp = dev;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found && !result && ret != -ENODEV) {
|
||||
dm_dbg("No match for node '%s'\n",
|
||||
fdt_get_name(blob, offset, NULL));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
86
u-boot/drivers/core/regmap.c
Normal file
86
u-boot/drivers/core/regmap.c
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <libfdt.h>
|
||||
#include <malloc.h>
|
||||
#include <mapmem.h>
|
||||
#include <regmap.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
int regmap_init_mem(struct udevice *dev, struct regmap **mapp)
|
||||
{
|
||||
const void *blob = gd->fdt_blob;
|
||||
struct regmap_range *range;
|
||||
const fdt32_t *cell;
|
||||
struct regmap *map;
|
||||
int count;
|
||||
int addr_len, size_len, both_len;
|
||||
int parent;
|
||||
int len;
|
||||
|
||||
parent = dev->parent->of_offset;
|
||||
addr_len = fdt_address_cells(blob, parent);
|
||||
size_len = fdt_size_cells(blob, parent);
|
||||
both_len = addr_len + size_len;
|
||||
|
||||
cell = fdt_getprop(blob, dev->of_offset, "reg", &len);
|
||||
len /= sizeof(*cell);
|
||||
count = len / both_len;
|
||||
if (!cell || !count)
|
||||
return -EINVAL;
|
||||
|
||||
map = malloc(sizeof(struct regmap));
|
||||
if (!map)
|
||||
return -ENOMEM;
|
||||
|
||||
if (count <= 1) {
|
||||
map->range = &map->base_range;
|
||||
} else {
|
||||
map->range = malloc(count * sizeof(struct regmap_range));
|
||||
if (!map->range) {
|
||||
free(map);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
map->base = fdtdec_get_number(cell, addr_len);
|
||||
map->range_count = count;
|
||||
|
||||
for (range = map->range; count > 0;
|
||||
count--, cell += both_len, range++) {
|
||||
range->start = fdtdec_get_number(cell, addr_len);
|
||||
range->size = fdtdec_get_number(cell + addr_len, size_len);
|
||||
}
|
||||
|
||||
*mapp = map;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *regmap_get_range(struct regmap *map, unsigned int range_num)
|
||||
{
|
||||
struct regmap_range *range;
|
||||
|
||||
if (range_num >= map->range_count)
|
||||
return NULL;
|
||||
range = &map->range[range_num];
|
||||
|
||||
return map_sysmem(range->start, range->size);
|
||||
}
|
||||
|
||||
int regmap_uninit(struct regmap *map)
|
||||
{
|
||||
if (map->range_count > 1)
|
||||
free(map->range);
|
||||
free(map);
|
||||
|
||||
return 0;
|
||||
}
|
||||
273
u-boot/drivers/core/root.c
Normal file
273
u-boot/drivers/core/root.c
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Google, Inc
|
||||
*
|
||||
* (C) Copyright 2012
|
||||
* Pavel Herrmann <morpheus.ibis@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <malloc.h>
|
||||
#include <libfdt.h>
|
||||
#include <dm/device.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/platdata.h>
|
||||
#include <dm/root.h>
|
||||
#include <dm/uclass.h>
|
||||
#include <dm/util.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
struct root_priv {
|
||||
fdt_addr_t translation_offset; /* optional translation offset */
|
||||
};
|
||||
|
||||
static const struct driver_info root_info = {
|
||||
.name = "root_driver",
|
||||
};
|
||||
|
||||
struct udevice *dm_root(void)
|
||||
{
|
||||
if (!gd->dm_root) {
|
||||
dm_warn("Virtual root driver does not exist!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return gd->dm_root;
|
||||
}
|
||||
|
||||
fdt_addr_t dm_get_translation_offset(void)
|
||||
{
|
||||
struct udevice *root = dm_root();
|
||||
struct root_priv *priv = dev_get_priv(root);
|
||||
|
||||
return priv->translation_offset;
|
||||
}
|
||||
|
||||
void dm_set_translation_offset(fdt_addr_t offs)
|
||||
{
|
||||
struct udevice *root = dm_root();
|
||||
struct root_priv *priv = dev_get_priv(root);
|
||||
|
||||
priv->translation_offset = offs;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_NEEDS_MANUAL_RELOC)
|
||||
void fix_drivers(void)
|
||||
{
|
||||
struct driver *drv =
|
||||
ll_entry_start(struct driver, driver);
|
||||
const int n_ents = ll_entry_count(struct driver, driver);
|
||||
struct driver *entry;
|
||||
|
||||
for (entry = drv; entry != drv + n_ents; entry++) {
|
||||
if (entry->of_match)
|
||||
entry->of_match = (const struct udevice_id *)
|
||||
((u32)entry->of_match + gd->reloc_off);
|
||||
if (entry->bind)
|
||||
entry->bind += gd->reloc_off;
|
||||
if (entry->probe)
|
||||
entry->probe += gd->reloc_off;
|
||||
if (entry->remove)
|
||||
entry->remove += gd->reloc_off;
|
||||
if (entry->unbind)
|
||||
entry->unbind += gd->reloc_off;
|
||||
if (entry->ofdata_to_platdata)
|
||||
entry->ofdata_to_platdata += gd->reloc_off;
|
||||
if (entry->child_post_bind)
|
||||
entry->child_post_bind += gd->reloc_off;
|
||||
if (entry->child_pre_probe)
|
||||
entry->child_pre_probe += gd->reloc_off;
|
||||
if (entry->child_post_remove)
|
||||
entry->child_post_remove += gd->reloc_off;
|
||||
/* OPS are fixed in every uclass post_probe function */
|
||||
if (entry->ops)
|
||||
entry->ops += gd->reloc_off;
|
||||
}
|
||||
}
|
||||
|
||||
void fix_uclass(void)
|
||||
{
|
||||
struct uclass_driver *uclass =
|
||||
ll_entry_start(struct uclass_driver, uclass);
|
||||
const int n_ents = ll_entry_count(struct uclass_driver, uclass);
|
||||
struct uclass_driver *entry;
|
||||
|
||||
for (entry = uclass; entry != uclass + n_ents; entry++) {
|
||||
if (entry->post_bind)
|
||||
entry->post_bind += gd->reloc_off;
|
||||
if (entry->pre_unbind)
|
||||
entry->pre_unbind += gd->reloc_off;
|
||||
if (entry->pre_probe)
|
||||
entry->pre_probe += gd->reloc_off;
|
||||
if (entry->post_probe)
|
||||
entry->post_probe += gd->reloc_off;
|
||||
if (entry->pre_remove)
|
||||
entry->pre_remove += gd->reloc_off;
|
||||
if (entry->child_post_bind)
|
||||
entry->child_post_bind += gd->reloc_off;
|
||||
if (entry->child_pre_probe)
|
||||
entry->child_pre_probe += gd->reloc_off;
|
||||
if (entry->init)
|
||||
entry->init += gd->reloc_off;
|
||||
if (entry->destroy)
|
||||
entry->destroy += gd->reloc_off;
|
||||
/* FIXME maybe also need to fix these ops */
|
||||
if (entry->ops)
|
||||
entry->ops += gd->reloc_off;
|
||||
}
|
||||
}
|
||||
|
||||
void fix_devices(void)
|
||||
{
|
||||
struct driver_info *dev =
|
||||
ll_entry_start(struct driver_info, driver_info);
|
||||
const int n_ents = ll_entry_count(struct driver_info, driver_info);
|
||||
struct driver_info *entry;
|
||||
|
||||
for (entry = dev; entry != dev + n_ents; entry++) {
|
||||
if (entry->platdata)
|
||||
entry->platdata += gd->reloc_off;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int dm_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (gd->dm_root) {
|
||||
dm_warn("Virtual root driver already exists!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
INIT_LIST_HEAD(&DM_UCLASS_ROOT_NON_CONST);
|
||||
|
||||
#if defined(CONFIG_NEEDS_MANUAL_RELOC)
|
||||
fix_drivers();
|
||||
fix_uclass();
|
||||
fix_devices();
|
||||
#endif
|
||||
|
||||
ret = device_bind_by_name(NULL, false, &root_info, &DM_ROOT_NON_CONST);
|
||||
if (ret)
|
||||
return ret;
|
||||
#if CONFIG_IS_ENABLED(OF_CONTROL)
|
||||
DM_ROOT_NON_CONST->of_offset = 0;
|
||||
#endif
|
||||
ret = device_probe(DM_ROOT_NON_CONST);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dm_uninit(void)
|
||||
{
|
||||
device_remove(dm_root());
|
||||
device_unbind(dm_root());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dm_scan_platdata(bool pre_reloc_only)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = lists_bind_drivers(DM_ROOT_NON_CONST, pre_reloc_only);
|
||||
if (ret == -ENOENT) {
|
||||
dm_warn("Some drivers were not found\n");
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(OF_CONTROL)
|
||||
int dm_scan_fdt_node(struct udevice *parent, const void *blob, int offset,
|
||||
bool pre_reloc_only)
|
||||
{
|
||||
int ret = 0, err;
|
||||
|
||||
for (offset = fdt_first_subnode(blob, offset);
|
||||
offset > 0;
|
||||
offset = fdt_next_subnode(blob, offset)) {
|
||||
if (pre_reloc_only &&
|
||||
!fdt_getprop(blob, offset, "u-boot,dm-pre-reloc", NULL))
|
||||
continue;
|
||||
if (!fdtdec_get_is_enabled(blob, offset)) {
|
||||
dm_dbg(" - ignoring disabled device\n");
|
||||
continue;
|
||||
}
|
||||
err = lists_bind_fdt(parent, blob, offset, NULL);
|
||||
if (err && !ret) {
|
||||
ret = err;
|
||||
debug("%s: ret=%d\n", fdt_get_name(blob, offset, NULL),
|
||||
ret);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret)
|
||||
dm_warn("Some drivers failed to bind\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dm_scan_fdt(const void *blob, bool pre_reloc_only)
|
||||
{
|
||||
return dm_scan_fdt_node(gd->dm_root, blob, 0, pre_reloc_only);
|
||||
}
|
||||
#endif
|
||||
|
||||
__weak int dm_scan_other(bool pre_reloc_only)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dm_init_and_scan(bool pre_reloc_only)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = dm_init();
|
||||
if (ret) {
|
||||
debug("dm_init() failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = dm_scan_platdata(pre_reloc_only);
|
||||
if (ret) {
|
||||
debug("dm_scan_platdata() failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (CONFIG_IS_ENABLED(OF_CONTROL)) {
|
||||
ret = dm_scan_fdt(gd->fdt_blob, pre_reloc_only);
|
||||
if (ret) {
|
||||
debug("dm_scan_fdt() failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = dm_scan_other(pre_reloc_only);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is the root driver - all drivers are children of this */
|
||||
U_BOOT_DRIVER(root_driver) = {
|
||||
.name = "root_driver",
|
||||
.id = UCLASS_ROOT,
|
||||
.priv_auto_alloc_size = sizeof(struct root_priv),
|
||||
};
|
||||
|
||||
/* This is the root uclass */
|
||||
UCLASS_DRIVER(root) = {
|
||||
.name = "root",
|
||||
.id = UCLASS_ROOT,
|
||||
};
|
||||
64
u-boot/drivers/core/simple-bus.c
Normal file
64
u-boot/drivers/core/simple-bus.c
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Google, Inc
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <dm/root.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
struct simple_bus_plat {
|
||||
u32 base;
|
||||
u32 size;
|
||||
u32 target;
|
||||
};
|
||||
|
||||
fdt_addr_t simple_bus_translate(struct udevice *dev, fdt_addr_t addr)
|
||||
{
|
||||
struct simple_bus_plat *plat = dev_get_uclass_platdata(dev);
|
||||
|
||||
if (addr >= plat->base && addr < plat->base + plat->size)
|
||||
addr = (addr - plat->base) + plat->target;
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
static int simple_bus_post_bind(struct udevice *dev)
|
||||
{
|
||||
u32 cell[3];
|
||||
int ret;
|
||||
|
||||
ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset, "ranges",
|
||||
cell, ARRAY_SIZE(cell));
|
||||
if (!ret) {
|
||||
struct simple_bus_plat *plat = dev_get_uclass_platdata(dev);
|
||||
|
||||
plat->base = cell[0];
|
||||
plat->target = cell[1];
|
||||
plat->size = cell[2];
|
||||
}
|
||||
|
||||
return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false);
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(simple_bus) = {
|
||||
.id = UCLASS_SIMPLE_BUS,
|
||||
.name = "simple_bus",
|
||||
.post_bind = simple_bus_post_bind,
|
||||
.per_device_platdata_auto_alloc_size = sizeof(struct simple_bus_plat),
|
||||
};
|
||||
|
||||
static const struct udevice_id generic_simple_bus_ids[] = {
|
||||
{ .compatible = "simple-bus" },
|
||||
{ .compatible = "simple-mfd" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(simple_bus_drv) = {
|
||||
.name = "generic_simple_bus",
|
||||
.id = UCLASS_SIMPLE_BUS,
|
||||
.of_match = generic_simple_bus_ids,
|
||||
};
|
||||
84
u-boot/drivers/core/syscon-uclass.c
Normal file
84
u-boot/drivers/core/syscon-uclass.c
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <syscon.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <regmap.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/root.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
struct regmap *syscon_get_regmap(struct udevice *dev)
|
||||
{
|
||||
struct syscon_uc_info *priv;
|
||||
|
||||
if (device_get_uclass_id(dev) != UCLASS_SYSCON)
|
||||
return ERR_PTR(-ENOEXEC);
|
||||
priv = dev_get_uclass_priv(dev);
|
||||
return priv->regmap;
|
||||
}
|
||||
|
||||
static int syscon_pre_probe(struct udevice *dev)
|
||||
{
|
||||
struct syscon_uc_info *priv = dev_get_uclass_priv(dev);
|
||||
|
||||
return regmap_init_mem(dev, &priv->regmap);
|
||||
}
|
||||
|
||||
int syscon_get_by_driver_data(ulong driver_data, struct udevice **devp)
|
||||
{
|
||||
struct udevice *dev;
|
||||
struct uclass *uc;
|
||||
int ret;
|
||||
|
||||
*devp = NULL;
|
||||
ret = uclass_get(UCLASS_SYSCON, &uc);
|
||||
if (ret)
|
||||
return ret;
|
||||
uclass_foreach_dev(dev, uc) {
|
||||
if (dev->driver_data == driver_data) {
|
||||
*devp = dev;
|
||||
return device_probe(dev);
|
||||
}
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
struct regmap *syscon_get_regmap_by_driver_data(ulong driver_data)
|
||||
{
|
||||
struct syscon_uc_info *priv;
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
ret = syscon_get_by_driver_data(driver_data, &dev);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
priv = dev_get_uclass_priv(dev);
|
||||
|
||||
return priv->regmap;
|
||||
}
|
||||
|
||||
void *syscon_get_first_range(ulong driver_data)
|
||||
{
|
||||
struct regmap *map;
|
||||
|
||||
map = syscon_get_regmap_by_driver_data(driver_data);
|
||||
if (IS_ERR(map))
|
||||
return map;
|
||||
return regmap_get_range(map, 0);
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(syscon) = {
|
||||
.id = UCLASS_SYSCON,
|
||||
.name = "syscon",
|
||||
.per_device_auto_alloc_size = sizeof(struct syscon_uc_info),
|
||||
.pre_probe = syscon_pre_probe,
|
||||
};
|
||||
550
u-boot/drivers/core/uclass.c
Normal file
550
u-boot/drivers/core/uclass.c
Normal file
@@ -0,0 +1,550 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Google, Inc
|
||||
*
|
||||
* (C) Copyright 2012
|
||||
* Pavel Herrmann <morpheus.ibis@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
#include <dm/device.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/uclass.h>
|
||||
#include <dm/uclass-internal.h>
|
||||
#include <dm/util.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
struct uclass *uclass_find(enum uclass_id key)
|
||||
{
|
||||
struct uclass *uc;
|
||||
|
||||
if (!gd->dm_root)
|
||||
return NULL;
|
||||
/*
|
||||
* TODO(sjg@chromium.org): Optimise this, perhaps moving the found
|
||||
* node to the start of the list, or creating a linear array mapping
|
||||
* id to node.
|
||||
*/
|
||||
list_for_each_entry(uc, &gd->uclass_root, sibling_node) {
|
||||
if (uc->uc_drv->id == key)
|
||||
return uc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* uclass_add() - Create new uclass in list
|
||||
* @id: Id number to create
|
||||
* @ucp: Returns pointer to uclass, or NULL on error
|
||||
* @return 0 on success, -ve on error
|
||||
*
|
||||
* The new uclass is added to the list. There must be only one uclass for
|
||||
* each id.
|
||||
*/
|
||||
static int uclass_add(enum uclass_id id, struct uclass **ucp)
|
||||
{
|
||||
struct uclass_driver *uc_drv;
|
||||
struct uclass *uc;
|
||||
int ret;
|
||||
|
||||
*ucp = NULL;
|
||||
uc_drv = lists_uclass_lookup(id);
|
||||
if (!uc_drv) {
|
||||
debug("Cannot find uclass for id %d: please add the UCLASS_DRIVER() declaration for this UCLASS_... id\n",
|
||||
id);
|
||||
/*
|
||||
* Use a strange error to make this case easier to find. When
|
||||
* a uclass is not available it can prevent driver model from
|
||||
* starting up and this failure is otherwise hard to debug.
|
||||
*/
|
||||
return -EPFNOSUPPORT;
|
||||
}
|
||||
uc = calloc(1, sizeof(*uc));
|
||||
if (!uc)
|
||||
return -ENOMEM;
|
||||
if (uc_drv->priv_auto_alloc_size) {
|
||||
uc->priv = calloc(1, uc_drv->priv_auto_alloc_size);
|
||||
if (!uc->priv) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_mem;
|
||||
}
|
||||
}
|
||||
uc->uc_drv = uc_drv;
|
||||
INIT_LIST_HEAD(&uc->sibling_node);
|
||||
INIT_LIST_HEAD(&uc->dev_head);
|
||||
list_add(&uc->sibling_node, &DM_UCLASS_ROOT_NON_CONST);
|
||||
|
||||
if (uc_drv->init) {
|
||||
ret = uc_drv->init(uc);
|
||||
if (ret)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
*ucp = uc;
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
if (uc_drv->priv_auto_alloc_size) {
|
||||
free(uc->priv);
|
||||
uc->priv = NULL;
|
||||
}
|
||||
list_del(&uc->sibling_node);
|
||||
fail_mem:
|
||||
free(uc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int uclass_destroy(struct uclass *uc)
|
||||
{
|
||||
struct uclass_driver *uc_drv;
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* We cannot use list_for_each_entry_safe() here. If a device in this
|
||||
* uclass has a child device also in this uclass, it will be also be
|
||||
* unbound (by the recursion in the call to device_unbind() below).
|
||||
* We can loop until the list is empty.
|
||||
*/
|
||||
while (!list_empty(&uc->dev_head)) {
|
||||
dev = list_first_entry(&uc->dev_head, struct udevice,
|
||||
uclass_node);
|
||||
ret = device_remove(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = device_unbind(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
uc_drv = uc->uc_drv;
|
||||
if (uc_drv->destroy)
|
||||
uc_drv->destroy(uc);
|
||||
list_del(&uc->sibling_node);
|
||||
if (uc_drv->priv_auto_alloc_size)
|
||||
free(uc->priv);
|
||||
free(uc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uclass_get(enum uclass_id id, struct uclass **ucp)
|
||||
{
|
||||
struct uclass *uc;
|
||||
|
||||
*ucp = NULL;
|
||||
uc = uclass_find(id);
|
||||
if (!uc)
|
||||
return uclass_add(id, ucp);
|
||||
*ucp = uc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uclass_find_device(enum uclass_id id, int index, struct udevice **devp)
|
||||
{
|
||||
struct uclass *uc;
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
*devp = NULL;
|
||||
ret = uclass_get(id, &uc);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (list_empty(&uc->dev_head))
|
||||
return -ENODEV;
|
||||
|
||||
list_for_each_entry(dev, &uc->dev_head, uclass_node) {
|
||||
if (!index--) {
|
||||
*devp = dev;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int uclass_find_first_device(enum uclass_id id, struct udevice **devp)
|
||||
{
|
||||
struct uclass *uc;
|
||||
int ret;
|
||||
|
||||
*devp = NULL;
|
||||
ret = uclass_get(id, &uc);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (list_empty(&uc->dev_head))
|
||||
return 0;
|
||||
|
||||
*devp = list_first_entry(&uc->dev_head, struct udevice, uclass_node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uclass_find_next_device(struct udevice **devp)
|
||||
{
|
||||
struct udevice *dev = *devp;
|
||||
|
||||
*devp = NULL;
|
||||
if (list_is_last(&dev->uclass_node, &dev->uclass->dev_head))
|
||||
return 0;
|
||||
|
||||
*devp = list_entry(dev->uclass_node.next, struct udevice, uclass_node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uclass_find_device_by_name(enum uclass_id id, const char *name,
|
||||
struct udevice **devp)
|
||||
{
|
||||
struct uclass *uc;
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
*devp = NULL;
|
||||
if (!name)
|
||||
return -EINVAL;
|
||||
ret = uclass_get(id, &uc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
list_for_each_entry(dev, &uc->dev_head, uclass_node) {
|
||||
if (!strncmp(dev->name, name, strlen(name))) {
|
||||
*devp = dev;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int uclass_find_device_by_seq(enum uclass_id id, int seq_or_req_seq,
|
||||
bool find_req_seq, struct udevice **devp)
|
||||
{
|
||||
struct uclass *uc;
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
*devp = NULL;
|
||||
debug("%s: %d %d\n", __func__, find_req_seq, seq_or_req_seq);
|
||||
if (seq_or_req_seq == -1)
|
||||
return -ENODEV;
|
||||
ret = uclass_get(id, &uc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
list_for_each_entry(dev, &uc->dev_head, uclass_node) {
|
||||
debug(" - %d %d\n", dev->req_seq, dev->seq);
|
||||
if ((find_req_seq ? dev->req_seq : dev->seq) ==
|
||||
seq_or_req_seq) {
|
||||
*devp = dev;
|
||||
debug(" - found\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
debug(" - not found\n");
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int uclass_find_device_by_of_offset(enum uclass_id id, int node,
|
||||
struct udevice **devp)
|
||||
{
|
||||
struct uclass *uc;
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
*devp = NULL;
|
||||
if (node < 0)
|
||||
return -ENODEV;
|
||||
ret = uclass_get(id, &uc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
list_for_each_entry(dev, &uc->dev_head, uclass_node) {
|
||||
if (dev->of_offset == node) {
|
||||
*devp = dev;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(OF_CONTROL)
|
||||
static int uclass_find_device_by_phandle(enum uclass_id id,
|
||||
struct udevice *parent,
|
||||
const char *name,
|
||||
struct udevice **devp)
|
||||
{
|
||||
struct udevice *dev;
|
||||
struct uclass *uc;
|
||||
int find_phandle;
|
||||
int ret;
|
||||
|
||||
*devp = NULL;
|
||||
find_phandle = fdtdec_get_int(gd->fdt_blob, parent->of_offset, name,
|
||||
-1);
|
||||
if (find_phandle <= 0)
|
||||
return -ENOENT;
|
||||
ret = uclass_get(id, &uc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
list_for_each_entry(dev, &uc->dev_head, uclass_node) {
|
||||
uint phandle = fdt_get_phandle(gd->fdt_blob, dev->of_offset);
|
||||
|
||||
if (phandle == find_phandle) {
|
||||
*devp = dev;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
int uclass_get_device_tail(struct udevice *dev, int ret,
|
||||
struct udevice **devp)
|
||||
{
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
assert(dev);
|
||||
ret = device_probe(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*devp = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uclass_get_device(enum uclass_id id, int index, struct udevice **devp)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
*devp = NULL;
|
||||
ret = uclass_find_device(id, index, &dev);
|
||||
return uclass_get_device_tail(dev, ret, devp);
|
||||
}
|
||||
|
||||
int uclass_get_device_by_name(enum uclass_id id, const char *name,
|
||||
struct udevice **devp)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
*devp = NULL;
|
||||
ret = uclass_find_device_by_name(id, name, &dev);
|
||||
return uclass_get_device_tail(dev, ret, devp);
|
||||
}
|
||||
|
||||
int uclass_get_device_by_seq(enum uclass_id id, int seq, struct udevice **devp)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
*devp = NULL;
|
||||
ret = uclass_find_device_by_seq(id, seq, false, &dev);
|
||||
if (ret == -ENODEV) {
|
||||
/*
|
||||
* We didn't find it in probed devices. See if there is one
|
||||
* that will request this seq if probed.
|
||||
*/
|
||||
ret = uclass_find_device_by_seq(id, seq, true, &dev);
|
||||
}
|
||||
return uclass_get_device_tail(dev, ret, devp);
|
||||
}
|
||||
|
||||
int uclass_get_device_by_of_offset(enum uclass_id id, int node,
|
||||
struct udevice **devp)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
*devp = NULL;
|
||||
ret = uclass_find_device_by_of_offset(id, node, &dev);
|
||||
return uclass_get_device_tail(dev, ret, devp);
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(OF_CONTROL)
|
||||
int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent,
|
||||
const char *name, struct udevice **devp)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
*devp = NULL;
|
||||
ret = uclass_find_device_by_phandle(id, parent, name, &dev);
|
||||
return uclass_get_device_tail(dev, ret, devp);
|
||||
}
|
||||
#endif
|
||||
|
||||
int uclass_first_device(enum uclass_id id, struct udevice **devp)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
*devp = NULL;
|
||||
ret = uclass_find_first_device(id, &dev);
|
||||
if (!dev)
|
||||
return 0;
|
||||
return uclass_get_device_tail(dev, ret, devp);
|
||||
}
|
||||
|
||||
int uclass_first_device_err(enum uclass_id id, struct udevice **devp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = uclass_first_device(id, devp);
|
||||
if (ret)
|
||||
return ret;
|
||||
else if (!*devp)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uclass_next_device(struct udevice **devp)
|
||||
{
|
||||
struct udevice *dev = *devp;
|
||||
int ret;
|
||||
|
||||
*devp = NULL;
|
||||
ret = uclass_find_next_device(&dev);
|
||||
if (!dev)
|
||||
return 0;
|
||||
return uclass_get_device_tail(dev, ret, devp);
|
||||
}
|
||||
|
||||
int uclass_bind_device(struct udevice *dev)
|
||||
{
|
||||
struct uclass *uc;
|
||||
int ret;
|
||||
|
||||
uc = dev->uclass;
|
||||
list_add_tail(&dev->uclass_node, &uc->dev_head);
|
||||
|
||||
if (dev->parent) {
|
||||
struct uclass_driver *uc_drv = dev->parent->uclass->uc_drv;
|
||||
|
||||
if (uc_drv->child_post_bind) {
|
||||
ret = uc_drv->child_post_bind(dev);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
/* There is no need to undo the parent's post_bind call */
|
||||
list_del(&dev->uclass_node);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)
|
||||
int uclass_unbind_device(struct udevice *dev)
|
||||
{
|
||||
struct uclass *uc;
|
||||
int ret;
|
||||
|
||||
uc = dev->uclass;
|
||||
if (uc->uc_drv->pre_unbind) {
|
||||
ret = uc->uc_drv->pre_unbind(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
list_del(&dev->uclass_node);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int uclass_resolve_seq(struct udevice *dev)
|
||||
{
|
||||
struct udevice *dup;
|
||||
int seq;
|
||||
int ret;
|
||||
|
||||
assert(dev->seq == -1);
|
||||
ret = uclass_find_device_by_seq(dev->uclass->uc_drv->id, dev->req_seq,
|
||||
false, &dup);
|
||||
if (!ret) {
|
||||
dm_warn("Device '%s': seq %d is in use by '%s'\n",
|
||||
dev->name, dev->req_seq, dup->name);
|
||||
} else if (ret == -ENODEV) {
|
||||
/* Our requested sequence number is available */
|
||||
if (dev->req_seq != -1)
|
||||
return dev->req_seq;
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (seq = 0; seq < DM_MAX_SEQ; seq++) {
|
||||
ret = uclass_find_device_by_seq(dev->uclass->uc_drv->id, seq,
|
||||
false, &dup);
|
||||
if (ret == -ENODEV)
|
||||
break;
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return seq;
|
||||
}
|
||||
|
||||
int uclass_pre_probe_device(struct udevice *dev)
|
||||
{
|
||||
struct uclass_driver *uc_drv;
|
||||
int ret;
|
||||
|
||||
uc_drv = dev->uclass->uc_drv;
|
||||
if (uc_drv->pre_probe) {
|
||||
ret = uc_drv->pre_probe(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!dev->parent)
|
||||
return 0;
|
||||
uc_drv = dev->parent->uclass->uc_drv;
|
||||
if (uc_drv->child_pre_probe)
|
||||
return uc_drv->child_pre_probe(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uclass_post_probe_device(struct udevice *dev)
|
||||
{
|
||||
struct uclass_driver *uc_drv = dev->uclass->uc_drv;
|
||||
|
||||
if (uc_drv->post_probe)
|
||||
return uc_drv->post_probe(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)
|
||||
int uclass_pre_remove_device(struct udevice *dev)
|
||||
{
|
||||
struct uclass *uc;
|
||||
int ret;
|
||||
|
||||
uc = dev->uclass;
|
||||
if (uc->uc_drv->pre_remove) {
|
||||
ret = uc->uc_drv->pre_remove(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
37
u-boot/drivers/core/util.c
Normal file
37
u-boot/drivers/core/util.c
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Google, Inc
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <vsprintf.h>
|
||||
|
||||
void dm_warn(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void dm_dbg(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
int list_count_items(struct list_head *head)
|
||||
{
|
||||
struct list_head *node;
|
||||
int count = 0;
|
||||
|
||||
list_for_each(node, head)
|
||||
count++;
|
||||
|
||||
return count;
|
||||
}
|
||||
Reference in New Issue
Block a user