2018-05-06 21:58:06 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2014-02-26 22:59:18 +00:00
|
|
|
/*
|
|
|
|
* Device manager
|
|
|
|
*
|
|
|
|
* Copyright (c) 2013 Google, Inc
|
|
|
|
*
|
|
|
|
* (C) Copyright 2012
|
|
|
|
* Pavel Herrmann <morpheus.ibis@gmail.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <common.h>
|
2019-11-14 19:57:39 +00:00
|
|
|
#include <cpu_func.h>
|
2020-05-10 17:40:05 +00:00
|
|
|
#include <log.h>
|
2020-10-31 03:38:53 +00:00
|
|
|
#include <asm/global_data.h>
|
2016-07-06 04:28:55 +00:00
|
|
|
#include <asm/io.h>
|
2018-01-08 12:59:18 +00:00
|
|
|
#include <clk.h>
|
2014-07-23 12:55:12 +00:00
|
|
|
#include <fdtdec.h>
|
2015-09-02 05:41:12 +00:00
|
|
|
#include <fdt_support.h>
|
2014-02-26 22:59:18 +00:00
|
|
|
#include <malloc.h>
|
2020-05-10 17:39:56 +00:00
|
|
|
#include <asm/cache.h>
|
2014-02-26 22:59:18 +00:00
|
|
|
#include <dm/device.h>
|
|
|
|
#include <dm/device-internal.h>
|
|
|
|
#include <dm/lists.h>
|
2018-01-15 10:07:20 +00:00
|
|
|
#include <dm/of_access.h>
|
pinctrl: add pin control uclass support
This creates a new framework for handling of pin control devices,
i.e. devices that control different aspects of package pins.
This uclass handles pinmuxing and pin configuration; pinmuxing
controls switching among silicon blocks that share certain physical
pins, pin configuration handles electronic properties such as pin-
biasing, load capacitance etc.
This framework can support the same device tree bindings, but if you
do not need full interface support, you can disable some features to
reduce memory foot print. Typically around 1.5KB is necessary to
include full-featured uclass support on ARM board (CONFIG_PINCTRL +
CONFIG_PINCTRL_FULL + CONFIG_PINCTRL_GENERIC + CONFIG_PINCTRL_PINMUX),
for example.
We are often limited on code size for SPL. Besides, we still have
many boards that do not support device tree configuration. The full
pinctrl, which requires OF_CONTROL, does not make sense for those
boards. So, this framework also has a Do-It-Yourself (let's say
simple pinctrl) interface. With CONFIG_PINCTRL_FULL disabled, the
uclass itself provides no systematic mechanism for identifying the
peripheral device, applying pinctrl settings, etc. They must be
done in each low-level driver. In return, you can save much memory
footprint and it might be useful especially for SPL.
Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
Acked-by: Simon Glass <sjg@chromium.org>
2015-08-27 03:44:29 +00:00
|
|
|
#include <dm/pinctrl.h>
|
2014-02-26 22:59:18 +00:00
|
|
|
#include <dm/platdata.h>
|
2017-05-19 02:09:05 +00:00
|
|
|
#include <dm/read.h>
|
2014-02-26 22:59:18 +00:00
|
|
|
#include <dm/uclass.h>
|
|
|
|
#include <dm/uclass-internal.h>
|
|
|
|
#include <dm/util.h>
|
2021-10-23 14:58:01 +00:00
|
|
|
#include <iommu.h>
|
2014-02-26 22:59:18 +00:00
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/list.h>
|
2018-07-27 02:20:38 +00:00
|
|
|
#include <power-domain.h>
|
2014-02-26 22:59:18 +00:00
|
|
|
|
2014-07-23 12:55:12 +00:00
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
|
2016-05-11 21:26:24 +00:00
|
|
|
static int device_bind_common(struct udevice *parent, const struct driver *drv,
|
2020-12-03 23:55:18 +00:00
|
|
|
const char *name, void *plat,
|
2017-05-17 23:18:11 +00:00
|
|
|
ulong driver_data, ofnode node,
|
2020-12-03 23:55:19 +00:00
|
|
|
uint of_plat_size, struct udevice **devp)
|
2014-02-26 22:59:18 +00:00
|
|
|
{
|
2014-05-22 10:43:05 +00:00
|
|
|
struct udevice *dev;
|
2014-02-26 22:59:18 +00:00
|
|
|
struct uclass *uc;
|
2015-04-15 11:07:18 +00:00
|
|
|
int size, ret = 0;
|
2020-12-17 04:20:09 +00:00
|
|
|
bool auto_seq = true;
|
2020-12-23 02:30:29 +00:00
|
|
|
void *ptr;
|
2014-02-26 22:59:18 +00:00
|
|
|
|
2021-03-15 04:25:15 +00:00
|
|
|
if (CONFIG_IS_ENABLED(OF_PLATDATA_NO_BIND))
|
|
|
|
return -ENOSYS;
|
|
|
|
|
2015-08-27 03:44:28 +00:00
|
|
|
if (devp)
|
|
|
|
*devp = NULL;
|
2014-02-26 22:59:18 +00:00
|
|
|
if (!name)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
ret = uclass_get(drv->id, &uc);
|
2015-08-30 22:55:16 +00:00
|
|
|
if (ret) {
|
|
|
|
debug("Missing uclass for driver %s\n", drv->name);
|
2014-02-26 22:59:18 +00:00
|
|
|
return ret;
|
2015-08-30 22:55:16 +00:00
|
|
|
}
|
2014-02-26 22:59:18 +00:00
|
|
|
|
2014-05-22 10:43:05 +00:00
|
|
|
dev = calloc(1, sizeof(struct udevice));
|
2014-02-26 22:59:18 +00:00
|
|
|
if (!dev)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&dev->sibling_node);
|
|
|
|
INIT_LIST_HEAD(&dev->child_head);
|
|
|
|
INIT_LIST_HEAD(&dev->uclass_node);
|
2015-07-25 12:52:37 +00:00
|
|
|
#ifdef CONFIG_DEVRES
|
devres: introduce Devres (Managed Device Resource) framework
In U-Boot's driver model, memory is basically allocated and freed
in the core framework. So, low level drivers generally only have
to specify the size of needed memory with .priv_auto_alloc_size,
.platdata_auto_alloc_size, etc. Nevertheless, some drivers still
need to allocate/free memory on their own in case they cannot
statically know the necessary memory size. So, I believe it is
reasonable enough to port Devres into U-boot.
Devres, which originates in Linux, manages device resources for each
device and automatically releases them on driver detach. With devres,
device resources are guaranteed to be freed whether initialization
fails half-way or the device gets detached.
The basic idea is totally the same to that of Linux, but I tweaked
it a bit so that it fits in U-Boot's driver model.
In U-Boot, drivers are activated in two steps: binding and probing.
Binding puts a driver and a device together. It is just data
manipulation on the system memory, so nothing has happened on the
hardware device at this moment. When the device is really used, it
is probed. Probing initializes the real hardware device to make it
really ready for use.
So, the resources acquired during the probing process must be freed
when the device is removed. Likewise, what has been allocated in
binding should be released when the device is unbound. The struct
devres has a member "probe" to remember when the resource was
allocated.
CONFIG_DEBUG_DEVRES is also supported for easier debugging.
If enabled, debug messages are printed each time a resource is
allocated/freed.
Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
Acked-by: Simon Glass <sjg@chromium.org>
2015-07-25 12:52:35 +00:00
|
|
|
INIT_LIST_HEAD(&dev->devres_head);
|
2015-07-25 12:52:37 +00:00
|
|
|
#endif
|
2020-12-23 02:30:29 +00:00
|
|
|
dev_set_plat(dev, plat);
|
2016-05-11 21:26:24 +00:00
|
|
|
dev->driver_data = driver_data;
|
2014-02-26 22:59:18 +00:00
|
|
|
dev->name = name;
|
2020-12-19 17:40:14 +00:00
|
|
|
dev_set_ofnode(dev, node);
|
2014-02-26 22:59:18 +00:00
|
|
|
dev->parent = parent;
|
|
|
|
dev->driver = drv;
|
|
|
|
dev->uclass = uc;
|
2014-07-23 12:55:12 +00:00
|
|
|
|
2020-12-19 17:40:09 +00:00
|
|
|
dev->seq_ = -1;
|
2018-12-07 13:50:39 +00:00
|
|
|
if (CONFIG_IS_ENABLED(DM_SEQ_ALIAS) &&
|
|
|
|
(uc->uc_drv->flags & DM_UC_FLAG_SEQ_ALIAS)) {
|
2015-02-28 05:06:30 +00:00
|
|
|
/*
|
2016-03-16 08:58:01 +00:00
|
|
|
* Some devices, such as a SPI bus, I2C bus and serial ports
|
|
|
|
* are numbered using aliases.
|
|
|
|
*/
|
2020-07-10 00:39:24 +00:00
|
|
|
if (CONFIG_IS_ENABLED(OF_CONTROL) &&
|
|
|
|
!CONFIG_IS_ENABLED(OF_PLATDATA)) {
|
2020-12-17 04:20:09 +00:00
|
|
|
if (uc->uc_drv->name && ofnode_valid(node)) {
|
2021-07-05 22:32:39 +00:00
|
|
|
if (!dev_read_alias_seq(dev, &dev->seq_)) {
|
2020-12-17 04:20:32 +00:00
|
|
|
auto_seq = false;
|
2021-07-05 22:32:39 +00:00
|
|
|
log_debug(" - seq=%d\n", dev->seq_);
|
|
|
|
}
|
2020-12-17 04:20:09 +00:00
|
|
|
}
|
2015-01-25 15:27:05 +00:00
|
|
|
}
|
2014-07-23 12:55:12 +00:00
|
|
|
}
|
2020-12-17 04:20:17 +00:00
|
|
|
if (auto_seq && !(uc->uc_drv->flags & DM_UC_FLAG_NO_AUTO_SEQ))
|
2020-12-19 17:40:09 +00:00
|
|
|
dev->seq_ = uclass_find_next_free_seq(uc);
|
2015-02-28 05:06:30 +00:00
|
|
|
|
2021-02-04 04:29:43 +00:00
|
|
|
/* Check if we need to allocate plat */
|
2020-12-03 23:55:18 +00:00
|
|
|
if (drv->plat_auto) {
|
|
|
|
bool alloc = !plat;
|
dm: core: Expand platdata for of-platdata devices
Devices which use of-platdata have their own platdata. However, in many
cases the driver will have its own auto-alloced platdata, for use with the
device tree. The ofdata_to_platdata() method converts the device tree
settings to platdata.
With of-platdata we would not normally allocate the platdata since it is
provided by the U_BOOT_DEVICE() declaration. However this is inconvenient
since the of-platdata struct is closely tied to the device tree properties.
It is unlikely to exactly match the platdata needed by the driver.
In fact a useful approach is to declare platdata in the driver like this:
struct r3288_mmc_platdata {
struct dtd_rockchip_rk3288_dw_mshc of_platdata;
/* the 'normal' fields go here */
};
In this case we have dt_platadata available, but the normal fields are not
present, since ofdata_to_platdata() is never called. In fact driver model
doesn't allocate any space for the 'normal' fields, since it sees that there
is already platform data attached to the device.
To make this easier, adjust driver model to allocate the full size of the
struct (i.e. platdata_auto_alloc_size from the driver) and copy in the
of-platdata. This means that when the driver's bind() method is called,
the of-platdata will be present, followed by zero bytes for the empty
'normal field' portion.
A new DM_FLAG_OF_PLATDATA flag is available that indicates that the platdata
came from of-platdata. When the allocation/copy happens, the
DM_FLAG_ALLOC_PDATA flag will be set as well. The dtoc tool is updated to
output the platdata_size field, since U-Boot has no other way of knowing
the size of the of-platdata struct.
Signed-off-by: Simon Glass <sjg@chromium.org>
2016-07-04 17:58:18 +00:00
|
|
|
|
2021-02-04 04:29:43 +00:00
|
|
|
/*
|
|
|
|
* For of-platdata, we try use the existing data, but if
|
|
|
|
* plat_auto is larger, we must allocate a new space
|
|
|
|
*/
|
dm: core: Expand platdata for of-platdata devices
Devices which use of-platdata have their own platdata. However, in many
cases the driver will have its own auto-alloced platdata, for use with the
device tree. The ofdata_to_platdata() method converts the device tree
settings to platdata.
With of-platdata we would not normally allocate the platdata since it is
provided by the U_BOOT_DEVICE() declaration. However this is inconvenient
since the of-platdata struct is closely tied to the device tree properties.
It is unlikely to exactly match the platdata needed by the driver.
In fact a useful approach is to declare platdata in the driver like this:
struct r3288_mmc_platdata {
struct dtd_rockchip_rk3288_dw_mshc of_platdata;
/* the 'normal' fields go here */
};
In this case we have dt_platadata available, but the normal fields are not
present, since ofdata_to_platdata() is never called. In fact driver model
doesn't allocate any space for the 'normal' fields, since it sees that there
is already platform data attached to the device.
To make this easier, adjust driver model to allocate the full size of the
struct (i.e. platdata_auto_alloc_size from the driver) and copy in the
of-platdata. This means that when the driver's bind() method is called,
the of-platdata will be present, followed by zero bytes for the empty
'normal field' portion.
A new DM_FLAG_OF_PLATDATA flag is available that indicates that the platdata
came from of-platdata. When the allocation/copy happens, the
DM_FLAG_ALLOC_PDATA flag will be set as well. The dtoc tool is updated to
output the platdata_size field, since U-Boot has no other way of knowing
the size of the of-platdata struct.
Signed-off-by: Simon Glass <sjg@chromium.org>
2016-07-04 17:58:18 +00:00
|
|
|
if (CONFIG_IS_ENABLED(OF_PLATDATA)) {
|
2021-02-04 04:29:43 +00:00
|
|
|
if (of_plat_size)
|
2020-12-19 17:40:10 +00:00
|
|
|
dev_or_flags(dev, DM_FLAG_OF_PLATDATA);
|
2021-02-04 04:29:43 +00:00
|
|
|
if (of_plat_size < drv->plat_auto)
|
|
|
|
alloc = true;
|
dm: core: Expand platdata for of-platdata devices
Devices which use of-platdata have their own platdata. However, in many
cases the driver will have its own auto-alloced platdata, for use with the
device tree. The ofdata_to_platdata() method converts the device tree
settings to platdata.
With of-platdata we would not normally allocate the platdata since it is
provided by the U_BOOT_DEVICE() declaration. However this is inconvenient
since the of-platdata struct is closely tied to the device tree properties.
It is unlikely to exactly match the platdata needed by the driver.
In fact a useful approach is to declare platdata in the driver like this:
struct r3288_mmc_platdata {
struct dtd_rockchip_rk3288_dw_mshc of_platdata;
/* the 'normal' fields go here */
};
In this case we have dt_platadata available, but the normal fields are not
present, since ofdata_to_platdata() is never called. In fact driver model
doesn't allocate any space for the 'normal' fields, since it sees that there
is already platform data attached to the device.
To make this easier, adjust driver model to allocate the full size of the
struct (i.e. platdata_auto_alloc_size from the driver) and copy in the
of-platdata. This means that when the driver's bind() method is called,
the of-platdata will be present, followed by zero bytes for the empty
'normal field' portion.
A new DM_FLAG_OF_PLATDATA flag is available that indicates that the platdata
came from of-platdata. When the allocation/copy happens, the
DM_FLAG_ALLOC_PDATA flag will be set as well. The dtoc tool is updated to
output the platdata_size field, since U-Boot has no other way of knowing
the size of the of-platdata struct.
Signed-off-by: Simon Glass <sjg@chromium.org>
2016-07-04 17:58:18 +00:00
|
|
|
}
|
|
|
|
if (alloc) {
|
2020-12-19 17:40:10 +00:00
|
|
|
dev_or_flags(dev, DM_FLAG_ALLOC_PDATA);
|
2020-12-23 02:30:29 +00:00
|
|
|
ptr = calloc(1, drv->plat_auto);
|
|
|
|
if (!ptr) {
|
dm: core: Expand platdata for of-platdata devices
Devices which use of-platdata have their own platdata. However, in many
cases the driver will have its own auto-alloced platdata, for use with the
device tree. The ofdata_to_platdata() method converts the device tree
settings to platdata.
With of-platdata we would not normally allocate the platdata since it is
provided by the U_BOOT_DEVICE() declaration. However this is inconvenient
since the of-platdata struct is closely tied to the device tree properties.
It is unlikely to exactly match the platdata needed by the driver.
In fact a useful approach is to declare platdata in the driver like this:
struct r3288_mmc_platdata {
struct dtd_rockchip_rk3288_dw_mshc of_platdata;
/* the 'normal' fields go here */
};
In this case we have dt_platadata available, but the normal fields are not
present, since ofdata_to_platdata() is never called. In fact driver model
doesn't allocate any space for the 'normal' fields, since it sees that there
is already platform data attached to the device.
To make this easier, adjust driver model to allocate the full size of the
struct (i.e. platdata_auto_alloc_size from the driver) and copy in the
of-platdata. This means that when the driver's bind() method is called,
the of-platdata will be present, followed by zero bytes for the empty
'normal field' portion.
A new DM_FLAG_OF_PLATDATA flag is available that indicates that the platdata
came from of-platdata. When the allocation/copy happens, the
DM_FLAG_ALLOC_PDATA flag will be set as well. The dtoc tool is updated to
output the platdata_size field, since U-Boot has no other way of knowing
the size of the of-platdata struct.
Signed-off-by: Simon Glass <sjg@chromium.org>
2016-07-04 17:58:18 +00:00
|
|
|
ret = -ENOMEM;
|
|
|
|
goto fail_alloc1;
|
|
|
|
}
|
2021-02-04 04:29:43 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* For of-platdata, copy the old plat into the new
|
|
|
|
* space
|
|
|
|
*/
|
2020-12-23 02:30:29 +00:00
|
|
|
if (CONFIG_IS_ENABLED(OF_PLATDATA) && plat)
|
|
|
|
memcpy(ptr, plat, of_plat_size);
|
|
|
|
dev_set_plat(dev, ptr);
|
2015-01-25 15:27:00 +00:00
|
|
|
}
|
|
|
|
}
|
2015-01-25 15:27:01 +00:00
|
|
|
|
2020-12-03 23:55:18 +00:00
|
|
|
size = uc->uc_drv->per_device_plat_auto;
|
2015-04-15 11:07:18 +00:00
|
|
|
if (size) {
|
2020-12-19 17:40:10 +00:00
|
|
|
dev_or_flags(dev, DM_FLAG_ALLOC_UCLASS_PDATA);
|
2020-12-23 02:30:29 +00:00
|
|
|
ptr = calloc(1, size);
|
|
|
|
if (!ptr) {
|
2015-04-15 11:07:18 +00:00
|
|
|
ret = -ENOMEM;
|
|
|
|
goto fail_alloc2;
|
|
|
|
}
|
2020-12-23 02:30:29 +00:00
|
|
|
dev_set_uclass_plat(dev, ptr);
|
2015-04-15 11:07:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (parent) {
|
2020-12-03 23:55:18 +00:00
|
|
|
size = parent->driver->per_child_plat_auto;
|
2021-02-04 04:29:44 +00:00
|
|
|
if (!size)
|
2020-12-03 23:55:18 +00:00
|
|
|
size = parent->uclass->uc_drv->per_child_plat_auto;
|
2015-01-25 15:27:01 +00:00
|
|
|
if (size) {
|
2020-12-19 17:40:10 +00:00
|
|
|
dev_or_flags(dev, DM_FLAG_ALLOC_PARENT_PDATA);
|
2020-12-23 02:30:29 +00:00
|
|
|
ptr = calloc(1, size);
|
|
|
|
if (!ptr) {
|
2015-01-25 15:27:01 +00:00
|
|
|
ret = -ENOMEM;
|
2015-04-15 11:07:18 +00:00
|
|
|
goto fail_alloc3;
|
2015-01-25 15:27:01 +00:00
|
|
|
}
|
2020-12-23 02:30:29 +00:00
|
|
|
dev_set_parent_plat(dev, ptr);
|
2015-01-25 15:27:01 +00:00
|
|
|
}
|
2020-02-15 20:38:48 +00:00
|
|
|
/* put dev into parent's successor list */
|
2014-02-26 22:59:18 +00:00
|
|
|
list_add_tail(&dev->sibling_node, &parent->child_head);
|
2020-02-15 20:38:48 +00:00
|
|
|
}
|
2014-02-26 22:59:18 +00:00
|
|
|
|
|
|
|
ret = uclass_bind_device(dev);
|
|
|
|
if (ret)
|
2015-01-25 15:26:59 +00:00
|
|
|
goto fail_uclass_bind;
|
2014-02-26 22:59:18 +00:00
|
|
|
|
|
|
|
/* if we fail to bind we remove device from successors and free it */
|
|
|
|
if (drv->bind) {
|
|
|
|
ret = drv->bind(dev);
|
2015-01-25 15:26:59 +00:00
|
|
|
if (ret)
|
2014-02-26 22:59:18 +00:00
|
|
|
goto fail_bind;
|
|
|
|
}
|
2015-01-25 15:27:03 +00:00
|
|
|
if (parent && parent->driver->child_post_bind) {
|
|
|
|
ret = parent->driver->child_post_bind(dev);
|
|
|
|
if (ret)
|
|
|
|
goto fail_child_post_bind;
|
|
|
|
}
|
2016-01-05 16:30:59 +00:00
|
|
|
if (uc->uc_drv->post_bind) {
|
|
|
|
ret = uc->uc_drv->post_bind(dev);
|
|
|
|
if (ret)
|
|
|
|
goto fail_uclass_post_bind;
|
|
|
|
}
|
2015-01-25 15:27:03 +00:00
|
|
|
|
2014-02-26 22:59:18 +00:00
|
|
|
if (parent)
|
2017-09-29 03:31:20 +00:00
|
|
|
pr_debug("Bound device %s to %s\n", dev->name, parent->name);
|
2015-08-27 03:44:28 +00:00
|
|
|
if (devp)
|
|
|
|
*devp = dev;
|
2014-02-26 22:59:18 +00:00
|
|
|
|
2020-12-19 17:40:10 +00:00
|
|
|
dev_or_flags(dev, DM_FLAG_BOUND);
|
2015-07-25 12:52:34 +00:00
|
|
|
|
2014-02-26 22:59:18 +00:00
|
|
|
return 0;
|
|
|
|
|
2016-01-05 16:30:59 +00:00
|
|
|
fail_uclass_post_bind:
|
|
|
|
/* There is no child unbind() method, so no clean-up required */
|
2015-01-25 15:27:03 +00:00
|
|
|
fail_child_post_bind:
|
2015-08-11 22:31:52 +00:00
|
|
|
if (CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)) {
|
2015-02-28 05:06:33 +00:00
|
|
|
if (drv->unbind && drv->unbind(dev)) {
|
|
|
|
dm_warn("unbind() method failed on dev '%s' on error path\n",
|
|
|
|
dev->name);
|
|
|
|
}
|
2015-01-25 15:27:03 +00:00
|
|
|
}
|
|
|
|
|
2014-02-26 22:59:18 +00:00
|
|
|
fail_bind:
|
2015-08-11 22:31:52 +00:00
|
|
|
if (CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)) {
|
2015-02-28 05:06:33 +00:00
|
|
|
if (uclass_unbind_device(dev)) {
|
|
|
|
dm_warn("Failed to unbind dev '%s' on error path\n",
|
|
|
|
dev->name);
|
|
|
|
}
|
2015-01-25 15:26:59 +00:00
|
|
|
}
|
|
|
|
fail_uclass_bind:
|
2015-08-11 22:31:52 +00:00
|
|
|
if (CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)) {
|
2015-02-28 05:06:33 +00:00
|
|
|
list_del(&dev->sibling_node);
|
2020-12-19 17:40:10 +00:00
|
|
|
if (dev_get_flags(dev) & DM_FLAG_ALLOC_PARENT_PDATA) {
|
2020-12-23 02:30:29 +00:00
|
|
|
free(dev_get_parent_plat(dev));
|
|
|
|
dev_set_parent_plat(dev, NULL);
|
2015-02-28 05:06:33 +00:00
|
|
|
}
|
2015-01-25 15:27:01 +00:00
|
|
|
}
|
2015-04-15 11:07:18 +00:00
|
|
|
fail_alloc3:
|
2021-02-04 04:29:44 +00:00
|
|
|
if (CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)) {
|
|
|
|
if (dev_get_flags(dev) & DM_FLAG_ALLOC_UCLASS_PDATA) {
|
|
|
|
free(dev_get_uclass_plat(dev));
|
|
|
|
dev_set_uclass_plat(dev, NULL);
|
|
|
|
}
|
2015-04-15 11:07:18 +00:00
|
|
|
}
|
2015-01-25 15:27:01 +00:00
|
|
|
fail_alloc2:
|
2021-02-04 04:29:44 +00:00
|
|
|
if (CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)) {
|
|
|
|
if (dev_get_flags(dev) & DM_FLAG_ALLOC_PDATA) {
|
|
|
|
free(dev_get_plat(dev));
|
|
|
|
dev_set_plat(dev, NULL);
|
|
|
|
}
|
2015-01-25 15:27:00 +00:00
|
|
|
}
|
|
|
|
fail_alloc1:
|
devres: introduce Devres (Managed Device Resource) framework
In U-Boot's driver model, memory is basically allocated and freed
in the core framework. So, low level drivers generally only have
to specify the size of needed memory with .priv_auto_alloc_size,
.platdata_auto_alloc_size, etc. Nevertheless, some drivers still
need to allocate/free memory on their own in case they cannot
statically know the necessary memory size. So, I believe it is
reasonable enough to port Devres into U-boot.
Devres, which originates in Linux, manages device resources for each
device and automatically releases them on driver detach. With devres,
device resources are guaranteed to be freed whether initialization
fails half-way or the device gets detached.
The basic idea is totally the same to that of Linux, but I tweaked
it a bit so that it fits in U-Boot's driver model.
In U-Boot, drivers are activated in two steps: binding and probing.
Binding puts a driver and a device together. It is just data
manipulation on the system memory, so nothing has happened on the
hardware device at this moment. When the device is really used, it
is probed. Probing initializes the real hardware device to make it
really ready for use.
So, the resources acquired during the probing process must be freed
when the device is removed. Likewise, what has been allocated in
binding should be released when the device is unbound. The struct
devres has a member "probe" to remember when the resource was
allocated.
CONFIG_DEBUG_DEVRES is also supported for easier debugging.
If enabled, debug messages are printed each time a resource is
allocated/freed.
Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
Acked-by: Simon Glass <sjg@chromium.org>
2015-07-25 12:52:35 +00:00
|
|
|
devres_release_all(dev);
|
|
|
|
|
2014-02-26 22:59:18 +00:00
|
|
|
free(dev);
|
2015-01-25 15:26:59 +00:00
|
|
|
|
2014-02-26 22:59:18 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-05-11 21:26:24 +00:00
|
|
|
int device_bind_with_driver_data(struct udevice *parent,
|
|
|
|
const struct driver *drv, const char *name,
|
2017-05-19 02:09:05 +00:00
|
|
|
ulong driver_data, ofnode node,
|
2016-05-11 21:26:24 +00:00
|
|
|
struct udevice **devp)
|
|
|
|
{
|
2017-05-19 02:09:05 +00:00
|
|
|
return device_bind_common(parent, drv, name, NULL, driver_data, node,
|
|
|
|
0, devp);
|
2016-05-11 21:26:24 +00:00
|
|
|
}
|
|
|
|
|
2020-11-29 00:50:01 +00:00
|
|
|
int device_bind(struct udevice *parent, const struct driver *drv,
|
2020-12-03 23:55:18 +00:00
|
|
|
const char *name, void *plat, ofnode node,
|
2020-11-29 00:50:01 +00:00
|
|
|
struct udevice **devp)
|
2018-06-11 19:07:15 +00:00
|
|
|
{
|
2020-12-03 23:55:18 +00:00
|
|
|
return device_bind_common(parent, drv, name, plat, 0, node, 0,
|
2018-06-11 19:07:15 +00:00
|
|
|
devp);
|
|
|
|
}
|
|
|
|
|
2014-07-23 12:55:03 +00:00
|
|
|
int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
|
2020-10-03 17:31:33 +00:00
|
|
|
const struct driver_info *info, struct udevice **devp)
|
2014-02-26 22:59:18 +00:00
|
|
|
{
|
|
|
|
struct driver *drv;
|
2020-12-03 23:55:19 +00:00
|
|
|
uint plat_size = 0;
|
2020-06-25 04:10:11 +00:00
|
|
|
int ret;
|
2014-02-26 22:59:18 +00:00
|
|
|
|
|
|
|
drv = lists_driver_lookup_name(info->name);
|
|
|
|
if (!drv)
|
|
|
|
return -ENOENT;
|
2014-07-23 12:55:03 +00:00
|
|
|
if (pre_reloc_only && !(drv->flags & DM_FLAG_PRE_RELOC))
|
|
|
|
return -EPERM;
|
2014-02-26 22:59:18 +00:00
|
|
|
|
dm: core: Expand platdata for of-platdata devices
Devices which use of-platdata have their own platdata. However, in many
cases the driver will have its own auto-alloced platdata, for use with the
device tree. The ofdata_to_platdata() method converts the device tree
settings to platdata.
With of-platdata we would not normally allocate the platdata since it is
provided by the U_BOOT_DEVICE() declaration. However this is inconvenient
since the of-platdata struct is closely tied to the device tree properties.
It is unlikely to exactly match the platdata needed by the driver.
In fact a useful approach is to declare platdata in the driver like this:
struct r3288_mmc_platdata {
struct dtd_rockchip_rk3288_dw_mshc of_platdata;
/* the 'normal' fields go here */
};
In this case we have dt_platadata available, but the normal fields are not
present, since ofdata_to_platdata() is never called. In fact driver model
doesn't allocate any space for the 'normal' fields, since it sees that there
is already platform data attached to the device.
To make this easier, adjust driver model to allocate the full size of the
struct (i.e. platdata_auto_alloc_size from the driver) and copy in the
of-platdata. This means that when the driver's bind() method is called,
the of-platdata will be present, followed by zero bytes for the empty
'normal field' portion.
A new DM_FLAG_OF_PLATDATA flag is available that indicates that the platdata
came from of-platdata. When the allocation/copy happens, the
DM_FLAG_ALLOC_PDATA flag will be set as well. The dtoc tool is updated to
output the platdata_size field, since U-Boot has no other way of knowing
the size of the of-platdata struct.
Signed-off-by: Simon Glass <sjg@chromium.org>
2016-07-04 17:58:18 +00:00
|
|
|
#if CONFIG_IS_ENABLED(OF_PLATDATA)
|
2020-12-03 23:55:19 +00:00
|
|
|
plat_size = info->plat_size;
|
dm: core: Expand platdata for of-platdata devices
Devices which use of-platdata have their own platdata. However, in many
cases the driver will have its own auto-alloced platdata, for use with the
device tree. The ofdata_to_platdata() method converts the device tree
settings to platdata.
With of-platdata we would not normally allocate the platdata since it is
provided by the U_BOOT_DEVICE() declaration. However this is inconvenient
since the of-platdata struct is closely tied to the device tree properties.
It is unlikely to exactly match the platdata needed by the driver.
In fact a useful approach is to declare platdata in the driver like this:
struct r3288_mmc_platdata {
struct dtd_rockchip_rk3288_dw_mshc of_platdata;
/* the 'normal' fields go here */
};
In this case we have dt_platadata available, but the normal fields are not
present, since ofdata_to_platdata() is never called. In fact driver model
doesn't allocate any space for the 'normal' fields, since it sees that there
is already platform data attached to the device.
To make this easier, adjust driver model to allocate the full size of the
struct (i.e. platdata_auto_alloc_size from the driver) and copy in the
of-platdata. This means that when the driver's bind() method is called,
the of-platdata will be present, followed by zero bytes for the empty
'normal field' portion.
A new DM_FLAG_OF_PLATDATA flag is available that indicates that the platdata
came from of-platdata. When the allocation/copy happens, the
DM_FLAG_ALLOC_PDATA flag will be set as well. The dtoc tool is updated to
output the platdata_size field, since U-Boot has no other way of knowing
the size of the of-platdata struct.
Signed-off-by: Simon Glass <sjg@chromium.org>
2016-07-04 17:58:18 +00:00
|
|
|
#endif
|
2020-12-03 23:55:18 +00:00
|
|
|
ret = device_bind_common(parent, drv, info->name, (void *)info->plat, 0,
|
2020-12-03 23:55:19 +00:00
|
|
|
ofnode_null(), plat_size, devp);
|
2020-06-25 04:10:11 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return ret;
|
2014-02-26 22:59:18 +00:00
|
|
|
}
|
|
|
|
|
2020-09-07 14:46:33 +00:00
|
|
|
int device_reparent(struct udevice *dev, struct udevice *new_parent)
|
|
|
|
{
|
|
|
|
struct udevice *pos, *n;
|
|
|
|
|
|
|
|
assert(dev);
|
|
|
|
assert(new_parent);
|
|
|
|
|
|
|
|
list_for_each_entry_safe(pos, n, &dev->parent->child_head,
|
|
|
|
sibling_node) {
|
|
|
|
if (pos->driver != dev->driver)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
list_del(&dev->sibling_node);
|
|
|
|
list_add_tail(&dev->sibling_node, &new_parent->child_head);
|
|
|
|
dev->parent = new_parent;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-03-25 18:21:53 +00:00
|
|
|
static void *alloc_priv(int size, uint flags)
|
|
|
|
{
|
|
|
|
void *priv;
|
|
|
|
|
|
|
|
if (flags & DM_FLAG_ALLOC_PRIV_DMA) {
|
2017-09-19 11:23:50 +00:00
|
|
|
size = ROUND(size, ARCH_DMA_MINALIGN);
|
2015-03-25 18:21:53 +00:00
|
|
|
priv = memalign(ARCH_DMA_MINALIGN, size);
|
2017-04-04 19:00:19 +00:00
|
|
|
if (priv) {
|
2015-03-25 18:21:53 +00:00
|
|
|
memset(priv, '\0', size);
|
2017-04-04 19:00:19 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Ensure that the zero bytes are flushed to memory.
|
|
|
|
* This prevents problems if the driver uses this as
|
|
|
|
* both an input and an output buffer:
|
|
|
|
*
|
|
|
|
* 1. Zeroes written to buffer (here) and sit in the
|
|
|
|
* cache
|
|
|
|
* 2. Driver issues a read command to DMA
|
|
|
|
* 3. CPU runs out of cache space and evicts some cache
|
|
|
|
* data in the buffer, writing zeroes to RAM from
|
|
|
|
* the memset() above
|
|
|
|
* 4. DMA completes
|
|
|
|
* 5. Buffer now has some DMA data and some zeroes
|
|
|
|
* 6. Data being read is now incorrect
|
|
|
|
*
|
|
|
|
* To prevent this, ensure that the cache is clean
|
|
|
|
* within this range at the start. The driver can then
|
|
|
|
* use normal flush-after-write, invalidate-before-read
|
|
|
|
* procedures.
|
|
|
|
*
|
|
|
|
* TODO(sjg@chromium.org): Drop this microblaze
|
|
|
|
* exception.
|
|
|
|
*/
|
|
|
|
#ifndef CONFIG_MICROBLAZE
|
|
|
|
flush_dcache_range((ulong)priv, (ulong)priv + size);
|
|
|
|
#endif
|
|
|
|
}
|
2015-03-25 18:21:53 +00:00
|
|
|
} else {
|
|
|
|
priv = calloc(1, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
return priv;
|
|
|
|
}
|
|
|
|
|
2020-12-19 17:40:08 +00:00
|
|
|
/**
|
|
|
|
* device_alloc_priv() - Allocate priv/plat data required by the device
|
|
|
|
*
|
|
|
|
* @dev: Device to process
|
2022-01-19 17:05:50 +00:00
|
|
|
* Return: 0 if OK, -ENOMEM if out of memory
|
2020-12-19 17:40:08 +00:00
|
|
|
*/
|
|
|
|
static int device_alloc_priv(struct udevice *dev)
|
2014-02-26 22:59:18 +00:00
|
|
|
{
|
2015-03-25 18:21:54 +00:00
|
|
|
const struct driver *drv;
|
2020-12-23 02:30:29 +00:00
|
|
|
void *ptr;
|
2020-12-19 17:40:08 +00:00
|
|
|
int size;
|
2020-04-05 21:38:19 +00:00
|
|
|
|
2014-02-26 22:59:18 +00:00
|
|
|
drv = dev->driver;
|
|
|
|
assert(drv);
|
|
|
|
|
2015-08-24 08:14:02 +00:00
|
|
|
/* Allocate private data if requested and not reentered */
|
2020-12-23 02:30:29 +00:00
|
|
|
if (drv->priv_auto && !dev_get_priv(dev)) {
|
|
|
|
ptr = alloc_priv(drv->priv_auto, drv->flags);
|
2020-12-19 17:40:08 +00:00
|
|
|
if (!ptr)
|
|
|
|
return -ENOMEM;
|
2020-12-23 02:30:29 +00:00
|
|
|
dev_set_priv(dev, ptr);
|
2014-02-26 22:59:18 +00:00
|
|
|
}
|
2020-12-19 17:40:08 +00:00
|
|
|
|
2015-08-24 08:14:02 +00:00
|
|
|
/* Allocate private data if requested and not reentered */
|
2020-12-03 23:55:17 +00:00
|
|
|
size = dev->uclass->uc_drv->per_device_auto;
|
2020-12-23 02:30:29 +00:00
|
|
|
if (size && !dev_get_uclass_priv(dev)) {
|
|
|
|
ptr = alloc_priv(size, dev->uclass->uc_drv->flags);
|
2020-12-19 17:40:08 +00:00
|
|
|
if (!ptr)
|
|
|
|
return -ENOMEM;
|
2020-12-23 02:30:29 +00:00
|
|
|
dev_set_uclass_priv(dev, ptr);
|
2014-02-26 22:59:18 +00:00
|
|
|
}
|
|
|
|
|
2019-12-30 04:19:18 +00:00
|
|
|
/* Allocate parent data for this child */
|
2014-02-26 22:59:18 +00:00
|
|
|
if (dev->parent) {
|
2020-12-03 23:55:17 +00:00
|
|
|
size = dev->parent->driver->per_child_auto;
|
2020-12-23 02:30:29 +00:00
|
|
|
if (!size)
|
2020-12-03 23:55:17 +00:00
|
|
|
size = dev->parent->uclass->uc_drv->per_child_auto;
|
2020-12-23 02:30:29 +00:00
|
|
|
if (size && !dev_get_parent_priv(dev)) {
|
|
|
|
ptr = alloc_priv(size, drv->flags);
|
2020-12-19 17:40:08 +00:00
|
|
|
if (!ptr)
|
|
|
|
return -ENOMEM;
|
2020-12-23 02:30:29 +00:00
|
|
|
dev_set_parent_priv(dev, ptr);
|
2014-07-23 12:55:20 +00:00
|
|
|
}
|
2019-12-30 04:19:18 +00:00
|
|
|
}
|
|
|
|
|
2020-12-19 17:40:08 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int device_of_to_plat(struct udevice *dev)
|
|
|
|
{
|
|
|
|
const struct driver *drv;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!dev)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2020-12-19 17:40:10 +00:00
|
|
|
if (dev_get_flags(dev) & DM_FLAG_PLATDATA_VALID)
|
2020-12-19 17:40:08 +00:00
|
|
|
return 0;
|
|
|
|
|
2021-03-15 04:25:15 +00:00
|
|
|
/*
|
|
|
|
* This is not needed if binding is disabled, since data is allocated
|
|
|
|
* at build time.
|
|
|
|
*/
|
|
|
|
if (!CONFIG_IS_ENABLED(OF_PLATDATA_NO_BIND)) {
|
|
|
|
/* Ensure all parents have ofdata */
|
|
|
|
if (dev->parent) {
|
|
|
|
ret = device_of_to_plat(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_get_flags(dev) & DM_FLAG_PLATDATA_VALID)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = device_alloc_priv(dev);
|
2020-12-19 17:40:08 +00:00
|
|
|
if (ret)
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
drv = dev->driver;
|
|
|
|
assert(drv);
|
|
|
|
|
2020-12-03 23:55:21 +00:00
|
|
|
if (drv->of_to_plat &&
|
2020-12-19 17:40:12 +00:00
|
|
|
(CONFIG_IS_ENABLED(OF_PLATDATA) || dev_has_ofnode(dev))) {
|
2020-12-03 23:55:21 +00:00
|
|
|
ret = drv->of_to_plat(dev);
|
2019-12-30 04:19:18 +00:00
|
|
|
if (ret)
|
|
|
|
goto fail;
|
|
|
|
}
|
2014-07-23 12:55:20 +00:00
|
|
|
|
2020-12-19 17:40:10 +00:00
|
|
|
dev_or_flags(dev, DM_FLAG_PLATDATA_VALID);
|
2019-12-30 04:19:21 +00:00
|
|
|
|
2019-12-30 04:19:20 +00:00
|
|
|
return 0;
|
|
|
|
fail:
|
|
|
|
device_free(dev);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-01-12 12:55:24 +00:00
|
|
|
/**
|
|
|
|
* device_get_dma_constraints() - Populate device's DMA constraints
|
|
|
|
*
|
|
|
|
* Gets a device's DMA constraints from firmware. This information is later
|
|
|
|
* used by drivers to translate physcal addresses to the device's bus address
|
|
|
|
* space. For now only device-tree is supported.
|
|
|
|
*
|
|
|
|
* @dev: Pointer to target device
|
|
|
|
* Return: 0 if OK or if no DMA constraints were found, error otherwise
|
|
|
|
*/
|
|
|
|
static int device_get_dma_constraints(struct udevice *dev)
|
|
|
|
{
|
|
|
|
struct udevice *parent = dev->parent;
|
|
|
|
phys_addr_t cpu = 0;
|
|
|
|
dma_addr_t bus = 0;
|
|
|
|
u64 size = 0;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!CONFIG_IS_ENABLED(DM_DMA) || !parent || !dev_has_ofnode(parent))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We start parsing for dma-ranges from the device's bus node. This is
|
|
|
|
* specially important on nested buses.
|
|
|
|
*/
|
|
|
|
ret = dev_get_dma_range(parent, &cpu, &bus, &size);
|
|
|
|
/* Don't return an error if no 'dma-ranges' were found */
|
|
|
|
if (ret && ret != -ENOENT) {
|
|
|
|
dm_warn("%s: failed to get DMA range, %d\n", dev->name, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_set_dma_offset(dev, cpu - bus);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-12-30 04:19:20 +00:00
|
|
|
int device_probe(struct udevice *dev)
|
|
|
|
{
|
|
|
|
const struct driver *drv;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!dev)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2020-12-19 17:40:10 +00:00
|
|
|
if (dev_get_flags(dev) & DM_FLAG_ACTIVATED)
|
2019-12-30 04:19:20 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
drv = dev->driver;
|
|
|
|
assert(drv);
|
|
|
|
|
2020-12-03 23:55:21 +00:00
|
|
|
ret = device_of_to_plat(dev);
|
2019-12-30 04:19:20 +00:00
|
|
|
if (ret)
|
|
|
|
goto fail;
|
|
|
|
|
2019-12-30 04:19:18 +00:00
|
|
|
/* Ensure all parents are probed */
|
|
|
|
if (dev->parent) {
|
2014-02-26 22:59:18 +00:00
|
|
|
ret = device_probe(dev->parent);
|
|
|
|
if (ret)
|
|
|
|
goto fail;
|
2015-08-24 08:14:02 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2020-12-19 17:40:10 +00:00
|
|
|
if (dev_get_flags(dev) & DM_FLAG_ACTIVATED)
|
2015-08-24 08:14:02 +00:00
|
|
|
return 0;
|
2014-02-26 22:59:18 +00:00
|
|
|
}
|
|
|
|
|
2020-12-19 17:40:10 +00:00
|
|
|
dev_or_flags(dev, DM_FLAG_ACTIVATED);
|
2015-03-25 18:21:56 +00:00
|
|
|
|
2022-01-07 15:38:09 +00:00
|
|
|
if (CONFIG_IS_ENABLED(POWER_DOMAIN) && dev->parent &&
|
|
|
|
(device_get_uclass_id(dev) != UCLASS_POWER_DOMAIN) &&
|
|
|
|
!(drv->flags & DM_FLAG_DEFAULT_PD_CTRL_OFF)) {
|
|
|
|
ret = dev_power_domain_on(dev);
|
|
|
|
if (ret)
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2015-09-12 14:45:19 +00:00
|
|
|
/*
|
|
|
|
* Process pinctrl for everything except the root device, and
|
2016-01-22 02:43:25 +00:00
|
|
|
* continue regardless of the result of pinctrl. Don't process pinctrl
|
|
|
|
* settings for pinctrl devices since the device may not yet be
|
|
|
|
* probed.
|
2021-01-21 20:57:13 +00:00
|
|
|
*
|
|
|
|
* This call can produce some non-intuitive results. For example, on an
|
|
|
|
* x86 device where dev is the main PCI bus, the pinctrl device may be
|
|
|
|
* child or grandchild of that bus, meaning that the child will be
|
|
|
|
* probed here. If the child happens to be the P2SB and the pinctrl
|
|
|
|
* device is a child of that, then both the pinctrl and P2SB will be
|
|
|
|
* probed by this call. This works because the DM_FLAG_ACTIVATED flag
|
|
|
|
* is set just above. However, the PCI bus' probe() method and
|
|
|
|
* associated uclass methods have not yet been called.
|
2015-09-12 14:45:19 +00:00
|
|
|
*/
|
2021-11-19 09:02:27 +00:00
|
|
|
if (dev->parent && device_get_uclass_id(dev) != UCLASS_PINCTRL) {
|
|
|
|
ret = pinctrl_select_state(dev, "default");
|
|
|
|
if (ret && ret != -ENOSYS)
|
|
|
|
log_debug("Device '%s' failed to configure default pinctrl: %d (%s)\n",
|
|
|
|
dev->name, ret, errno_str(ret));
|
|
|
|
}
|
pinctrl: add pin control uclass support
This creates a new framework for handling of pin control devices,
i.e. devices that control different aspects of package pins.
This uclass handles pinmuxing and pin configuration; pinmuxing
controls switching among silicon blocks that share certain physical
pins, pin configuration handles electronic properties such as pin-
biasing, load capacitance etc.
This framework can support the same device tree bindings, but if you
do not need full interface support, you can disable some features to
reduce memory foot print. Typically around 1.5KB is necessary to
include full-featured uclass support on ARM board (CONFIG_PINCTRL +
CONFIG_PINCTRL_FULL + CONFIG_PINCTRL_GENERIC + CONFIG_PINCTRL_PINMUX),
for example.
We are often limited on code size for SPL. Besides, we still have
many boards that do not support device tree configuration. The full
pinctrl, which requires OF_CONTROL, does not make sense for those
boards. So, this framework also has a Do-It-Yourself (let's say
simple pinctrl) interface. With CONFIG_PINCTRL_FULL disabled, the
uclass itself provides no systematic mechanism for identifying the
peripheral device, applying pinctrl settings, etc. They must be
done in each low-level driver. In return, you can save much memory
footprint and it might be useful especially for SPL.
Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
Acked-by: Simon Glass <sjg@chromium.org>
2015-08-27 03:44:29 +00:00
|
|
|
|
2021-10-23 14:58:01 +00:00
|
|
|
if (CONFIG_IS_ENABLED(IOMMU) && dev->parent &&
|
|
|
|
(device_get_uclass_id(dev) != UCLASS_IOMMU)) {
|
|
|
|
ret = dev_iommu_enable(dev);
|
|
|
|
if (ret)
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2021-01-12 12:55:24 +00:00
|
|
|
ret = device_get_dma_constraints(dev);
|
|
|
|
if (ret)
|
|
|
|
goto fail;
|
|
|
|
|
2015-03-05 19:25:22 +00:00
|
|
|
ret = uclass_pre_probe_device(dev);
|
2015-01-25 15:27:10 +00:00
|
|
|
if (ret)
|
|
|
|
goto fail;
|
|
|
|
|
2014-07-23 12:55:21 +00:00
|
|
|
if (dev->parent && dev->parent->driver->child_pre_probe) {
|
|
|
|
ret = dev->parent->driver->child_pre_probe(dev);
|
|
|
|
if (ret)
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2019-07-05 16:23:16 +00:00
|
|
|
/* Only handle devices that have a valid ofnode */
|
2020-12-19 17:40:13 +00:00
|
|
|
if (dev_has_ofnode(dev)) {
|
2019-07-05 16:23:16 +00:00
|
|
|
/*
|
|
|
|
* Process 'assigned-{clocks/clock-parents/clock-rates}'
|
|
|
|
* properties
|
|
|
|
*/
|
2021-06-11 04:16:07 +00:00
|
|
|
ret = clk_set_defaults(dev, CLK_DEFAULTS_PRE);
|
2019-07-05 16:23:16 +00:00
|
|
|
if (ret)
|
|
|
|
goto fail;
|
|
|
|
}
|
2018-01-08 12:59:18 +00:00
|
|
|
|
2014-02-26 22:59:18 +00:00
|
|
|
if (drv->probe) {
|
|
|
|
ret = drv->probe(dev);
|
2019-12-30 04:19:16 +00:00
|
|
|
if (ret)
|
2014-02-26 22:59:18 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = uclass_post_probe_device(dev);
|
2015-03-25 18:21:56 +00:00
|
|
|
if (ret)
|
2014-02-26 22:59:18 +00:00
|
|
|
goto fail_uclass;
|
|
|
|
|
2021-11-19 09:02:27 +00:00
|
|
|
if (dev->parent && device_get_uclass_id(dev) == UCLASS_PINCTRL) {
|
|
|
|
ret = pinctrl_select_state(dev, "default");
|
|
|
|
if (ret && ret != -ENOSYS)
|
|
|
|
log_debug("Device '%s' failed to configure default pinctrl: %d (%s)\n",
|
|
|
|
dev->name, ret, errno_str(ret));
|
|
|
|
}
|
2016-03-12 05:17:38 +00:00
|
|
|
|
2014-02-26 22:59:18 +00:00
|
|
|
return 0;
|
|
|
|
fail_uclass:
|
2017-03-20 11:51:48 +00:00
|
|
|
if (device_remove(dev, DM_REMOVE_NORMAL)) {
|
2014-02-26 22:59:18 +00:00
|
|
|
dm_warn("%s: Device '%s' failed to remove on error path\n",
|
|
|
|
__func__, dev->name);
|
|
|
|
}
|
|
|
|
fail:
|
2020-12-19 17:40:10 +00:00
|
|
|
dev_bic_flags(dev, DM_FLAG_ACTIVATED);
|
2015-03-25 18:21:56 +00:00
|
|
|
|
2014-02-26 22:59:18 +00:00
|
|
|
device_free(dev);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-12-03 23:55:20 +00:00
|
|
|
void *dev_get_plat(const struct udevice *dev)
|
2014-02-26 22:59:18 +00:00
|
|
|
{
|
|
|
|
if (!dev) {
|
2014-12-10 15:55:56 +00:00
|
|
|
dm_warn("%s: null device\n", __func__);
|
2014-02-26 22:59:18 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-03-15 04:25:40 +00:00
|
|
|
return dm_priv_to_rw(dev->plat_);
|
2014-02-26 22:59:18 +00:00
|
|
|
}
|
|
|
|
|
2020-12-03 23:55:18 +00:00
|
|
|
void *dev_get_parent_plat(const struct udevice *dev)
|
2015-01-25 15:27:01 +00:00
|
|
|
{
|
|
|
|
if (!dev) {
|
2015-07-06 22:47:40 +00:00
|
|
|
dm_warn("%s: null device\n", __func__);
|
2015-01-25 15:27:01 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-03-15 04:25:40 +00:00
|
|
|
return dm_priv_to_rw(dev->parent_plat_);
|
2015-01-25 15:27:01 +00:00
|
|
|
}
|
|
|
|
|
2020-12-03 23:55:18 +00:00
|
|
|
void *dev_get_uclass_plat(const struct udevice *dev)
|
2015-04-15 11:07:18 +00:00
|
|
|
{
|
|
|
|
if (!dev) {
|
2015-07-06 22:47:40 +00:00
|
|
|
dm_warn("%s: null device\n", __func__);
|
2015-04-15 11:07:18 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-03-15 04:25:40 +00:00
|
|
|
return dm_priv_to_rw(dev->uclass_plat_);
|
2015-04-15 11:07:18 +00:00
|
|
|
}
|
|
|
|
|
2018-10-01 18:22:06 +00:00
|
|
|
void *dev_get_priv(const struct udevice *dev)
|
2014-02-26 22:59:18 +00:00
|
|
|
{
|
|
|
|
if (!dev) {
|
2014-12-10 15:55:56 +00:00
|
|
|
dm_warn("%s: null device\n", __func__);
|
2014-02-26 22:59:18 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-03-15 04:25:40 +00:00
|
|
|
return dm_priv_to_rw(dev->priv_);
|
2014-02-26 22:59:18 +00:00
|
|
|
}
|
2014-07-23 12:55:19 +00:00
|
|
|
|
2018-10-01 18:22:06 +00:00
|
|
|
void *dev_get_uclass_priv(const struct udevice *dev)
|
2015-03-05 19:25:20 +00:00
|
|
|
{
|
|
|
|
if (!dev) {
|
|
|
|
dm_warn("%s: null device\n", __func__);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-03-15 04:25:40 +00:00
|
|
|
return dm_priv_to_rw(dev->uclass_priv_);
|
2015-03-05 19:25:20 +00:00
|
|
|
}
|
|
|
|
|
2018-10-01 18:22:06 +00:00
|
|
|
void *dev_get_parent_priv(const struct udevice *dev)
|
2014-07-23 12:55:20 +00:00
|
|
|
{
|
|
|
|
if (!dev) {
|
2014-12-10 15:55:56 +00:00
|
|
|
dm_warn("%s: null device\n", __func__);
|
2014-07-23 12:55:20 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-03-15 04:25:40 +00:00
|
|
|
return dm_priv_to_rw(dev->parent_priv_);
|
2014-07-23 12:55:20 +00:00
|
|
|
}
|
|
|
|
|
2014-07-23 12:55:19 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-08-07 13:24:03 +00:00
|
|
|
#if CONFIG_IS_ENABLED(OF_REAL)
|
2018-06-26 06:46:50 +00:00
|
|
|
/**
|
|
|
|
* device_find_by_ofnode() - Return device associated with given ofnode
|
|
|
|
*
|
|
|
|
* The returned device is *not* activated.
|
|
|
|
*
|
|
|
|
* @node: The ofnode for which a associated device should be looked up
|
|
|
|
* @devp: Pointer to structure to hold the found device
|
|
|
|
* Return: 0 if OK, -ve on error
|
|
|
|
*/
|
|
|
|
static int device_find_by_ofnode(ofnode node, struct udevice **devp)
|
|
|
|
{
|
|
|
|
struct uclass *uc;
|
|
|
|
struct udevice *dev;
|
|
|
|
int ret;
|
|
|
|
|
2020-12-19 17:40:17 +00:00
|
|
|
list_for_each_entry(uc, gd->uclass_root, sibling_node) {
|
2018-06-26 06:46:50 +00:00
|
|
|
ret = uclass_find_device_by_ofnode(uc->uc_drv->id, node,
|
|
|
|
&dev);
|
|
|
|
if (!ret || dev) {
|
|
|
|
*devp = dev;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
2019-08-31 16:03:28 +00:00
|
|
|
#endif
|
2018-06-26 06:46:50 +00:00
|
|
|
|
2020-01-27 15:49:36 +00:00
|
|
|
int device_get_child(const struct udevice *parent, int index,
|
|
|
|
struct udevice **devp)
|
2014-07-23 12:55:19 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-01-27 15:49:36 +00:00
|
|
|
int device_get_child_count(const struct udevice *parent)
|
2019-09-04 10:31:26 +00:00
|
|
|
{
|
|
|
|
struct udevice *dev;
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
list_for_each_entry(dev, &parent->child_head, sibling_node)
|
|
|
|
count++;
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2021-12-17 03:59:32 +00:00
|
|
|
int device_get_decendent_count(const struct udevice *parent)
|
|
|
|
{
|
|
|
|
const struct udevice *dev;
|
|
|
|
int count = 1;
|
|
|
|
|
|
|
|
list_for_each_entry(dev, &parent->child_head, sibling_node)
|
|
|
|
count += device_get_decendent_count(dev);
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2020-12-17 04:20:29 +00:00
|
|
|
int device_find_child_by_seq(const struct udevice *parent, int seq,
|
|
|
|
struct udevice **devp)
|
2014-07-23 12:55:19 +00:00
|
|
|
{
|
|
|
|
struct udevice *dev;
|
|
|
|
|
|
|
|
*devp = NULL;
|
|
|
|
|
|
|
|
list_for_each_entry(dev, &parent->child_head, sibling_node) {
|
2020-12-19 17:40:09 +00:00
|
|
|
if (dev->seq_ == seq) {
|
2014-07-23 12:55:19 +00:00
|
|
|
*devp = dev;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2020-01-27 15:49:36 +00:00
|
|
|
int device_get_child_by_seq(const struct udevice *parent, int seq,
|
2014-07-23 12:55:19 +00:00
|
|
|
struct udevice **devp)
|
|
|
|
{
|
|
|
|
struct udevice *dev;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
*devp = NULL;
|
2020-12-17 04:20:29 +00:00
|
|
|
ret = device_find_child_by_seq(parent, seq, &dev);
|
|
|
|
|
2014-07-23 12:55:19 +00:00
|
|
|
return device_get_device_tail(dev, ret, devp);
|
|
|
|
}
|
|
|
|
|
2020-01-27 15:49:36 +00:00
|
|
|
int device_find_child_by_of_offset(const struct udevice *parent, int of_offset,
|
2014-07-23 12:55:19 +00:00
|
|
|
struct udevice **devp)
|
|
|
|
{
|
|
|
|
struct udevice *dev;
|
|
|
|
|
|
|
|
*devp = NULL;
|
|
|
|
|
|
|
|
list_for_each_entry(dev, &parent->child_head, sibling_node) {
|
2017-01-17 23:52:55 +00:00
|
|
|
if (dev_of_offset(dev) == of_offset) {
|
2014-07-23 12:55:19 +00:00
|
|
|
*devp = dev;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2020-01-27 15:49:36 +00:00
|
|
|
int device_get_child_by_of_offset(const struct udevice *parent, int node,
|
2014-07-23 12:55:19 +00:00
|
|
|
struct udevice **devp)
|
|
|
|
{
|
|
|
|
struct udevice *dev;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
*devp = NULL;
|
2015-06-23 21:38:38 +00:00
|
|
|
ret = device_find_child_by_of_offset(parent, node, &dev);
|
2014-07-23 12:55:19 +00:00
|
|
|
return device_get_device_tail(dev, ret, devp);
|
|
|
|
}
|
2014-10-14 05:41:49 +00:00
|
|
|
|
2018-08-09 14:17:44 +00:00
|
|
|
static struct udevice *_device_find_global_by_ofnode(struct udevice *parent,
|
|
|
|
ofnode ofnode)
|
2015-06-23 21:38:37 +00:00
|
|
|
{
|
|
|
|
struct udevice *dev, *found;
|
|
|
|
|
2018-08-09 14:17:44 +00:00
|
|
|
if (ofnode_equal(dev_ofnode(parent), ofnode))
|
2015-06-23 21:38:37 +00:00
|
|
|
return parent;
|
|
|
|
|
|
|
|
list_for_each_entry(dev, &parent->child_head, sibling_node) {
|
2018-08-09 14:17:44 +00:00
|
|
|
found = _device_find_global_by_ofnode(dev, ofnode);
|
2015-06-23 21:38:37 +00:00
|
|
|
if (found)
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-08-09 14:17:44 +00:00
|
|
|
int device_find_global_by_ofnode(ofnode ofnode, struct udevice **devp)
|
|
|
|
{
|
|
|
|
*devp = _device_find_global_by_ofnode(gd->dm_root, ofnode);
|
|
|
|
|
|
|
|
return *devp ? 0 : -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
int device_get_global_by_ofnode(ofnode ofnode, struct udevice **devp)
|
2015-06-23 21:38:37 +00:00
|
|
|
{
|
|
|
|
struct udevice *dev;
|
|
|
|
|
2018-08-09 14:17:44 +00:00
|
|
|
dev = _device_find_global_by_ofnode(gd->dm_root, ofnode);
|
2015-06-23 21:38:37 +00:00
|
|
|
return device_get_device_tail(dev, dev ? 0 : -ENOENT, devp);
|
|
|
|
}
|
|
|
|
|
2020-06-25 04:10:11 +00:00
|
|
|
#if CONFIG_IS_ENABLED(OF_PLATDATA)
|
2021-03-15 04:25:28 +00:00
|
|
|
int device_get_by_ofplat_idx(uint idx, struct udevice **devp)
|
2020-10-03 17:31:40 +00:00
|
|
|
{
|
|
|
|
struct udevice *dev;
|
|
|
|
|
2021-03-15 04:25:28 +00:00
|
|
|
if (CONFIG_IS_ENABLED(OF_PLATDATA_INST)) {
|
|
|
|
struct udevice *base = ll_entry_start(struct udevice, udevice);
|
|
|
|
|
|
|
|
dev = base + idx;
|
|
|
|
} else {
|
|
|
|
struct driver_rt *drt = gd_dm_driver_rt() + idx;
|
|
|
|
|
|
|
|
dev = drt->dev;
|
|
|
|
}
|
2020-10-03 17:31:40 +00:00
|
|
|
*devp = NULL;
|
|
|
|
|
|
|
|
return device_get_device_tail(dev, dev ? 0 : -ENOENT, devp);
|
|
|
|
}
|
2020-06-25 04:10:11 +00:00
|
|
|
#endif
|
|
|
|
|
2020-01-27 15:49:36 +00:00
|
|
|
int device_find_first_child(const struct udevice *parent, struct udevice **devp)
|
2014-10-14 05:41:49 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
2014-11-11 17:46:18 +00:00
|
|
|
|
2020-01-27 15:49:36 +00:00
|
|
|
int device_find_first_inactive_child(const struct udevice *parent,
|
2018-10-01 18:22:07 +00:00
|
|
|
enum uclass_id uclass_id,
|
|
|
|
struct udevice **devp)
|
|
|
|
{
|
|
|
|
struct udevice *dev;
|
|
|
|
|
|
|
|
*devp = NULL;
|
|
|
|
list_for_each_entry(dev, &parent->child_head, sibling_node) {
|
|
|
|
if (!device_active(dev) &&
|
|
|
|
device_get_uclass_id(dev) == uclass_id) {
|
|
|
|
*devp = dev;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2020-01-27 15:49:36 +00:00
|
|
|
int device_find_first_child_by_uclass(const struct udevice *parent,
|
2018-11-18 15:14:31 +00:00
|
|
|
enum uclass_id uclass_id,
|
|
|
|
struct udevice **devp)
|
|
|
|
{
|
|
|
|
struct udevice *dev;
|
|
|
|
|
|
|
|
*devp = NULL;
|
|
|
|
list_for_each_entry(dev, &parent->child_head, sibling_node) {
|
|
|
|
if (device_get_uclass_id(dev) == uclass_id) {
|
|
|
|
*devp = dev;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2021-10-23 23:26:08 +00:00
|
|
|
int device_find_child_by_namelen(const struct udevice *parent, const char *name,
|
|
|
|
int len, struct udevice **devp)
|
2018-11-18 15:14:31 +00:00
|
|
|
{
|
|
|
|
struct udevice *dev;
|
|
|
|
|
|
|
|
*devp = NULL;
|
|
|
|
|
|
|
|
list_for_each_entry(dev, &parent->child_head, sibling_node) {
|
2021-10-23 23:26:08 +00:00
|
|
|
if (!strncmp(dev->name, name, len) &&
|
|
|
|
strlen(dev->name) == len) {
|
2018-11-18 15:14:31 +00:00
|
|
|
*devp = dev;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2021-10-23 23:26:08 +00:00
|
|
|
int device_find_child_by_name(const struct udevice *parent, const char *name,
|
|
|
|
struct udevice **devp)
|
|
|
|
{
|
|
|
|
return device_find_child_by_namelen(parent, name, strlen(name), devp);
|
|
|
|
}
|
|
|
|
|
2020-01-27 15:49:48 +00:00
|
|
|
int device_first_child_err(struct udevice *parent, struct udevice **devp)
|
|
|
|
{
|
|
|
|
struct udevice *dev;
|
|
|
|
|
|
|
|
device_find_first_child(parent, &dev);
|
|
|
|
if (!dev)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
return device_get_device_tail(dev, 0, devp);
|
|
|
|
}
|
|
|
|
|
|
|
|
int device_next_child_err(struct udevice **devp)
|
|
|
|
{
|
|
|
|
struct udevice *dev = *devp;
|
|
|
|
|
|
|
|
device_find_next_child(&dev);
|
|
|
|
if (!dev)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
return device_get_device_tail(dev, 0, devp);
|
|
|
|
}
|
|
|
|
|
2020-01-27 15:49:47 +00:00
|
|
|
int device_first_child_ofdata_err(struct udevice *parent, struct udevice **devp)
|
|
|
|
{
|
|
|
|
struct udevice *dev;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
device_find_first_child(parent, &dev);
|
|
|
|
if (!dev)
|
|
|
|
return -ENODEV;
|
|
|
|
|
2020-12-03 23:55:21 +00:00
|
|
|
ret = device_of_to_plat(dev);
|
2020-01-27 15:49:47 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
*devp = dev;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int device_next_child_ofdata_err(struct udevice **devp)
|
|
|
|
{
|
|
|
|
struct udevice *dev = *devp;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
device_find_next_child(&dev);
|
|
|
|
if (!dev)
|
|
|
|
return -ENODEV;
|
|
|
|
|
2020-12-03 23:55:21 +00:00
|
|
|
ret = device_of_to_plat(dev);
|
2020-01-27 15:49:47 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
*devp = dev;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-10-01 18:22:06 +00:00
|
|
|
struct udevice *dev_get_parent(const struct udevice *child)
|
2014-11-11 17:46:19 +00:00
|
|
|
{
|
|
|
|
return child->parent;
|
|
|
|
}
|
|
|
|
|
2018-10-01 18:22:06 +00:00
|
|
|
ulong dev_get_driver_data(const struct udevice *dev)
|
2014-11-11 17:46:18 +00:00
|
|
|
{
|
2015-03-25 18:21:55 +00:00
|
|
|
return dev->driver_data;
|
2014-11-11 17:46:18 +00:00
|
|
|
}
|
2015-01-25 15:27:04 +00:00
|
|
|
|
2018-10-01 18:22:06 +00:00
|
|
|
const void *dev_get_driver_ops(const struct udevice *dev)
|
2015-04-15 11:07:24 +00:00
|
|
|
{
|
|
|
|
if (!dev || !dev->driver->ops)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return dev->driver->ops;
|
|
|
|
}
|
|
|
|
|
2018-10-01 18:22:06 +00:00
|
|
|
enum uclass_id device_get_uclass_id(const struct udevice *dev)
|
2015-01-25 15:27:04 +00:00
|
|
|
{
|
|
|
|
return dev->uclass->uc_drv->id;
|
|
|
|
}
|
2015-02-10 06:46:32 +00:00
|
|
|
|
2018-10-01 18:22:06 +00:00
|
|
|
const char *dev_get_uclass_name(const struct udevice *dev)
|
2015-04-15 11:07:25 +00:00
|
|
|
{
|
|
|
|
if (!dev)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return dev->uclass->uc_drv->name;
|
|
|
|
}
|
|
|
|
|
2018-10-01 18:22:06 +00:00
|
|
|
bool device_has_children(const struct udevice *dev)
|
2015-03-25 18:21:57 +00:00
|
|
|
{
|
|
|
|
return !list_empty(&dev->child_head);
|
|
|
|
}
|
|
|
|
|
2020-01-27 15:49:36 +00:00
|
|
|
bool device_has_active_children(const struct udevice *dev)
|
2015-03-25 18:21:57 +00:00
|
|
|
{
|
|
|
|
struct udevice *child;
|
|
|
|
|
|
|
|
for (device_find_first_child(dev, &child);
|
|
|
|
child;
|
|
|
|
device_find_next_child(&child)) {
|
|
|
|
if (device_active(child))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-01-27 15:49:36 +00:00
|
|
|
bool device_is_last_sibling(const struct udevice *dev)
|
2015-03-25 18:21:57 +00:00
|
|
|
{
|
|
|
|
struct udevice *parent = dev->parent;
|
|
|
|
|
|
|
|
if (!parent)
|
|
|
|
return false;
|
|
|
|
return list_is_last(&dev->sibling_node, &parent->child_head);
|
|
|
|
}
|
2015-07-30 19:40:39 +00:00
|
|
|
|
2016-05-01 19:52:23 +00:00
|
|
|
void device_set_name_alloced(struct udevice *dev)
|
|
|
|
{
|
2020-12-19 17:40:10 +00:00
|
|
|
dev_or_flags(dev, DM_FLAG_NAME_ALLOCED);
|
2016-05-01 19:52:23 +00:00
|
|
|
}
|
|
|
|
|
2015-07-30 19:40:39 +00:00
|
|
|
int device_set_name(struct udevice *dev, const char *name)
|
|
|
|
{
|
|
|
|
name = strdup(name);
|
|
|
|
if (!name)
|
|
|
|
return -ENOMEM;
|
|
|
|
dev->name = name;
|
2016-05-01 19:52:23 +00:00
|
|
|
device_set_name_alloced(dev);
|
2015-07-30 19:40:39 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2016-04-28 10:06:02 +00:00
|
|
|
|
2020-12-23 02:30:27 +00:00
|
|
|
void dev_set_priv(struct udevice *dev, void *priv)
|
|
|
|
{
|
2020-12-23 02:30:30 +00:00
|
|
|
dev->priv_ = priv;
|
2020-12-23 02:30:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void dev_set_parent_priv(struct udevice *dev, void *parent_priv)
|
|
|
|
{
|
2020-12-23 02:30:30 +00:00
|
|
|
dev->parent_priv_ = parent_priv;
|
2020-12-23 02:30:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void dev_set_uclass_priv(struct udevice *dev, void *uclass_priv)
|
|
|
|
{
|
2020-12-23 02:30:30 +00:00
|
|
|
dev->uclass_priv_ = uclass_priv;
|
2020-12-23 02:30:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void dev_set_plat(struct udevice *dev, void *plat)
|
|
|
|
{
|
2020-12-23 02:30:30 +00:00
|
|
|
dev->plat_ = plat;
|
2020-12-23 02:30:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void dev_set_parent_plat(struct udevice *dev, void *parent_plat)
|
|
|
|
{
|
2020-12-23 02:30:30 +00:00
|
|
|
dev->parent_plat_ = parent_plat;
|
2020-12-23 02:30:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void dev_set_uclass_plat(struct udevice *dev, void *uclass_plat)
|
|
|
|
{
|
2020-12-23 02:30:30 +00:00
|
|
|
dev->uclass_plat_ = uclass_plat;
|
2020-12-23 02:30:27 +00:00
|
|
|
}
|
|
|
|
|
2021-08-07 13:24:03 +00:00
|
|
|
#if CONFIG_IS_ENABLED(OF_REAL)
|
2020-01-27 15:49:36 +00:00
|
|
|
bool device_is_compatible(const struct udevice *dev, const char *compat)
|
2016-04-28 10:06:02 +00:00
|
|
|
{
|
2018-04-19 03:14:02 +00:00
|
|
|
return ofnode_device_is_compatible(dev_ofnode(dev), compat);
|
2016-04-28 10:06:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool of_machine_is_compatible(const char *compat)
|
|
|
|
{
|
|
|
|
const void *fdt = gd->fdt_blob;
|
|
|
|
|
|
|
|
return !fdt_node_check_compatible(fdt, 0, compat);
|
|
|
|
}
|
2018-06-26 06:46:50 +00:00
|
|
|
|
|
|
|
int dev_disable_by_path(const char *path)
|
|
|
|
{
|
|
|
|
struct uclass *uc;
|
|
|
|
ofnode node = ofnode_path(path);
|
|
|
|
struct udevice *dev;
|
|
|
|
int ret = 1;
|
|
|
|
|
|
|
|
if (!of_live_active())
|
|
|
|
return -ENOSYS;
|
|
|
|
|
2020-12-19 17:40:17 +00:00
|
|
|
list_for_each_entry(uc, gd->uclass_root, sibling_node) {
|
2018-06-26 06:46:50 +00:00
|
|
|
ret = uclass_find_device_by_ofnode(uc->uc_drv->id, node, &dev);
|
|
|
|
if (!ret)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = device_remove(dev, DM_REMOVE_NORMAL);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = device_unbind(dev);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return ofnode_set_enabled(node, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
int dev_enable_by_path(const char *path)
|
|
|
|
{
|
|
|
|
ofnode node = ofnode_path(path);
|
|
|
|
ofnode pnode = ofnode_get_parent(node);
|
|
|
|
struct udevice *parent;
|
|
|
|
int ret = 1;
|
|
|
|
|
|
|
|
if (!of_live_active())
|
|
|
|
return -ENOSYS;
|
|
|
|
|
|
|
|
ret = device_find_by_ofnode(pnode, &parent);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = ofnode_set_enabled(node, true);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2021-09-10 14:16:20 +00:00
|
|
|
return lists_bind_fdt(parent, node, NULL, NULL, false);
|
2018-06-26 06:46:50 +00:00
|
|
|
}
|
2019-08-31 16:03:28 +00:00
|
|
|
#endif
|
2021-03-15 04:25:37 +00:00
|
|
|
|
|
|
|
#if CONFIG_IS_ENABLED(OF_PLATDATA_RT)
|
|
|
|
static struct udevice_rt *dev_get_rt(const struct udevice *dev)
|
|
|
|
{
|
|
|
|
struct udevice *base = ll_entry_start(struct udevice, udevice);
|
|
|
|
int idx = dev - base;
|
|
|
|
|
|
|
|
struct udevice_rt *urt = gd_dm_udevice_rt() + idx;
|
|
|
|
|
|
|
|
return urt;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 dev_get_flags(const struct udevice *dev)
|
|
|
|
{
|
|
|
|
const struct udevice_rt *urt = dev_get_rt(dev);
|
|
|
|
|
|
|
|
return urt->flags_;
|
|
|
|
}
|
|
|
|
|
|
|
|
void dev_or_flags(const struct udevice *dev, u32 or)
|
|
|
|
{
|
|
|
|
struct udevice_rt *urt = dev_get_rt(dev);
|
|
|
|
|
|
|
|
urt->flags_ |= or;
|
|
|
|
}
|
|
|
|
|
|
|
|
void dev_bic_flags(const struct udevice *dev, u32 bic)
|
|
|
|
{
|
|
|
|
struct udevice_rt *urt = dev_get_rt(dev);
|
|
|
|
|
|
|
|
urt->flags_ &= ~bic;
|
|
|
|
}
|
|
|
|
#endif /* OF_PLATDATA_RT */
|