Support late device removal

Allow booting a 32-bit system with a top memory address beyond 4 GiB
 -----BEGIN PGP SIGNATURE-----
 
 iQFFBAABCgAvFiEEslwAIq+Gp8wWVbYnfxc6PpAIreYFAmAbVFARHHNqZ0BjaHJv
 bWl1bS5vcmcACgkQfxc6PpAIrebn/Af9FoJksV/0G+z6AzjJ20+yoOVAOFFhUkFB
 UTW5m0Sv3wMmlSNdg0+DPuB3o04U6RwlZTdIqK/shc0fEqr0YeJgWAEPSMjQH0xc
 ou8S9LAnJNrvlqNu5aP2Wq5KFwOxp/ODx5RBNpiSaL+m26dmUi2eeU+Ym996rr06
 +m0qycZP07BHLIfm0pWyZjAI2+VzR7Uuyd1pKEIerOTRkAvZzk6pQbs+vv6PLaHa
 Wcl9grnoO3pDZt6CYpmgv8mbetXteRfrdYMsu2OiHx/2nU3pa7TbOoNcuA2ww2Ko
 u9NL7s0E0Y1LY6/QNb1O7eGOxcR1H5y/19K6NGrK27BwMkrvb0GKRg==
 =jMvm
 -----END PGP SIGNATURE-----

Merge tag 'dm-pull-3feb21' of https://gitlab.denx.de/u-boot/custodians/u-boot-dm

Support late device removal
Allow booting a 32-bit system with a top memory address beyond 4 GiB
This commit is contained in:
Tom Rini 2021-02-04 08:19:23 -05:00
commit 21cb717e79
30 changed files with 306 additions and 104 deletions

View File

@ -15,23 +15,23 @@ void arch_print_bdinfo(void)
{
struct bd_info *bd = gd->bd;
bdinfo_print_num("arch_number", bd->bi_arch_number);
bdinfo_print_num_l("arch_number", bd->bi_arch_number);
#ifdef CONFIG_SYS_MEM_RESERVE_SECURE
if (gd->arch.secure_ram & MEM_RESERVE_SECURE_SECURED) {
bdinfo_print_num("Secure ram",
gd->arch.secure_ram &
MEM_RESERVE_SECURE_ADDR_MASK);
bdinfo_print_num_ll("Secure ram",
gd->arch.secure_ram &
MEM_RESERVE_SECURE_ADDR_MASK);
}
#endif
#ifdef CONFIG_RESV_RAM
if (gd->arch.resv_ram)
bdinfo_print_num("Reserved ram", gd->arch.resv_ram);
bdinfo_print_num_ll("Reserved ram", gd->arch.resv_ram);
#endif
#if !(CONFIG_IS_ENABLED(SYS_ICACHE_OFF) && CONFIG_IS_ENABLED(SYS_DCACHE_OFF))
bdinfo_print_num("TLB addr", gd->arch.tlb_addr);
bdinfo_print_num_l("TLB addr", gd->arch.tlb_addr);
#endif
bdinfo_print_num("irq_sp", gd->irq_sp); /* irq stack pointer */
bdinfo_print_num("sp start ", gd->start_addr_sp);
bdinfo_print_num_l("irq_sp", gd->irq_sp); /* irq stack pointer */
bdinfo_print_num_l("sp start ", gd->start_addr_sp);
/*
* TODO: Currently only support for davinci SOC's is added.
* Remove this check once all the board implement this.

View File

@ -119,6 +119,9 @@ static void announce_and_cleanup(int fake)
* This may be useful for last-stage operations, like cancelling
* of DMA operation or releasing device internal buffers.
*/
dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL | DM_REMOVE_NON_VITAL);
/* Remove all active vital devices next */
dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL);
cleanup_before_linux();

View File

@ -37,7 +37,7 @@ struct tos_parameter_t {
int dram_init_banksize(void)
{
size_t top = min((unsigned long)(gd->ram_size + CONFIG_SYS_SDRAM_BASE),
gd->ram_top);
(unsigned long)(gd->ram_top));
#ifdef CONFIG_ARM64
/* Reserve 0x200000 for ATF bl31 */

View File

@ -38,7 +38,7 @@ void arch_print_bdinfo(void)
bdinfo_print_mhz("busfreq", bd->bi_busfreq);
#if defined(CONFIG_SYS_MBAR)
bdinfo_print_num("mbar", bd->bi_mbar_base);
bdinfo_print_num_l("mbar", bd->bi_mbar_base);
#endif
bdinfo_print_mhz("cpufreq", bd->bi_intfreq);
if (IS_ENABLED(CONFIG_PCI))

View File

@ -47,9 +47,9 @@ void arch_print_bdinfo(void)
bdinfo_print_mhz("busfreq", bd->bi_busfreq);
#if defined(CONFIG_MPC8xx) || defined(CONFIG_E500)
bdinfo_print_num("immr_base", bd->bi_immr_base);
bdinfo_print_num_l("immr_base", bd->bi_immr_base);
#endif
bdinfo_print_num("bootflags", bd->bi_bootflags);
bdinfo_print_num_l("bootflags", bd->bi_bootflags);
bdinfo_print_mhz("intfreq", bd->bi_intfreq);
#ifdef CONFIG_ENABLE_36BIT_PHYS
if (IS_ENABLED(CONFIG_PHYS_64BIT))

View File

@ -22,7 +22,6 @@ int dram_init_banksize(void)
ulong board_get_usable_ram_top(ulong total_size)
{
#ifdef CONFIG_64BIT
/*
* Ensure that we run from first 4GB so that all
* addresses used by U-Boot are 32bit addresses.
@ -31,8 +30,8 @@ ulong board_get_usable_ram_top(ulong total_size)
* devices work fine because DMA mapping APIs will
* provide 32bit DMA addresses only.
*/
if (gd->ram_top > SZ_4G)
return SZ_4G;
#endif
if (gd->ram_top >= SZ_4G)
return SZ_4G - 1;
return gd->ram_top;
}

View File

@ -22,7 +22,6 @@ int dram_init_banksize(void)
ulong board_get_usable_ram_top(ulong total_size)
{
#ifdef CONFIG_64BIT
/*
* Ensure that we run from first 4GB so that all
* addresses used by U-Boot are 32bit addresses.
@ -31,8 +30,8 @@ ulong board_get_usable_ram_top(ulong total_size)
* devices work fine because DMA mapping APIs will
* provide 32bit DMA addresses only.
*/
if (gd->ram_top > SZ_4G)
return SZ_4G;
#endif
if (gd->ram_top >= SZ_4G)
return SZ_4G - 1;
return gd->ram_top;
}

View File

@ -35,8 +35,8 @@ typedef u64 dma_addr_t;
typedef u32 dma_addr_t;
#endif
typedef unsigned long phys_addr_t;
typedef unsigned long phys_size_t;
typedef unsigned long long phys_addr_t;
typedef unsigned long long phys_size_t;
#endif /* __KERNEL__ */

View File

@ -77,7 +77,7 @@ int smc_init(void)
if (addr == FDT_ADDR_T_NONE)
return -EINVAL;
regs = (struct ftsmc020_bank *)addr;
regs = (struct ftsmc020_bank *)(uintptr_t)addr;
regs->cr &= ~FTSMC020_BANK_WPROT;
return 0;

View File

@ -18,11 +18,16 @@
DECLARE_GLOBAL_DATA_PTR;
void bdinfo_print_num(const char *name, ulong value)
void bdinfo_print_num_l(const char *name, ulong value)
{
printf("%-12s= 0x%0*lx\n", name, 2 * (int)sizeof(value), value);
}
void bdinfo_print_num_ll(const char *name, unsigned long long value)
{
printf("%-12s= 0x%.*llx\n", name, 2 * (int)sizeof(ulong), value);
}
static void print_eth(int idx)
{
char name[10], *val;
@ -36,12 +41,6 @@ static void print_eth(int idx)
printf("%-12s= %s\n", name, val);
}
static void print_phys_addr(const char *name, phys_addr_t value)
{
printf("%-12s= 0x%.*llx\n", name, 2 * (int)sizeof(ulong),
(unsigned long long)value);
}
void bdinfo_print_mhz(const char *name, unsigned long hz)
{
char buf[32];
@ -55,9 +54,9 @@ static void print_bi_dram(const struct bd_info *bd)
for (i = 0; i < CONFIG_NR_DRAM_BANKS; ++i) {
if (bd->bi_dram[i].size) {
bdinfo_print_num("DRAM bank", i);
bdinfo_print_num("-> start", bd->bi_dram[i].start);
bdinfo_print_num("-> size", bd->bi_dram[i].size);
bdinfo_print_num_l("DRAM bank", i);
bdinfo_print_num_ll("-> start", bd->bi_dram[i].start);
bdinfo_print_num_ll("-> size", bd->bi_dram[i].size);
}
}
}
@ -77,9 +76,10 @@ static void show_video_info(void)
if (device_active(dev)) {
struct video_priv *upriv = dev_get_uclass_priv(dev);
print_phys_addr("FB base", (ulong)upriv->fb);
bdinfo_print_num_ll("FB base", (ulong)upriv->fb);
if (upriv->copy_fb)
print_phys_addr("FB copy", (ulong)upriv->copy_fb);
bdinfo_print_num_ll("FB copy",
(ulong)upriv->copy_fb);
printf("%-12s= %dx%dx%d\n", "FB size", upriv->xsize,
upriv->ysize, 1 << upriv->bpix);
}
@ -91,36 +91,36 @@ int do_bdinfo(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
struct bd_info *bd = gd->bd;
#ifdef DEBUG
bdinfo_print_num("bd address", (ulong)bd);
bdinfo_print_num_l("bd address", (ulong)bd);
#endif
bdinfo_print_num("boot_params", (ulong)bd->bi_boot_params);
bdinfo_print_num_l("boot_params", (ulong)bd->bi_boot_params);
print_bi_dram(bd);
if (IS_ENABLED(CONFIG_SYS_HAS_SRAM)) {
bdinfo_print_num("sramstart", (ulong)bd->bi_sramstart);
bdinfo_print_num("sramsize", (ulong)bd->bi_sramsize);
bdinfo_print_num_l("sramstart", (ulong)bd->bi_sramstart);
bdinfo_print_num_l("sramsize", (ulong)bd->bi_sramsize);
}
bdinfo_print_num("flashstart", (ulong)bd->bi_flashstart);
bdinfo_print_num("flashsize", (ulong)bd->bi_flashsize);
bdinfo_print_num("flashoffset", (ulong)bd->bi_flashoffset);
bdinfo_print_num_l("flashstart", (ulong)bd->bi_flashstart);
bdinfo_print_num_l("flashsize", (ulong)bd->bi_flashsize);
bdinfo_print_num_l("flashoffset", (ulong)bd->bi_flashoffset);
printf("baudrate = %u bps\n", gd->baudrate);
bdinfo_print_num("relocaddr", gd->relocaddr);
bdinfo_print_num("reloc off", gd->reloc_off);
bdinfo_print_num_l("relocaddr", gd->relocaddr);
bdinfo_print_num_l("reloc off", gd->reloc_off);
printf("%-12s= %u-bit\n", "Build", (uint)sizeof(void *) * 8);
if (IS_ENABLED(CONFIG_CMD_NET)) {
printf("current eth = %s\n", eth_get_name());
print_eth(0);
printf("IP addr = %s\n", env_get("ipaddr"));
}
bdinfo_print_num("fdt_blob", (ulong)gd->fdt_blob);
bdinfo_print_num("new_fdt", (ulong)gd->new_fdt);
bdinfo_print_num("fdt_size", (ulong)gd->fdt_size);
bdinfo_print_num_l("fdt_blob", (ulong)gd->fdt_blob);
bdinfo_print_num_l("new_fdt", (ulong)gd->new_fdt);
bdinfo_print_num_l("fdt_size", (ulong)gd->fdt_size);
if (IS_ENABLED(CONFIG_DM_VIDEO))
show_video_info();
#if defined(CONFIG_LCD) || defined(CONFIG_VIDEO)
bdinfo_print_num("FB base ", gd->fb_base);
bdinfo_print_num_l("FB base ", gd->fb_base);
#endif
#if CONFIG_IS_ENABLED(MULTI_DTB_FIT)
bdinfo_print_num("multi_dtb_fit", (ulong)gd->multi_dtb_fit);
bdinfo_print_num_l("multi_dtb_fit", (ulong)gd->multi_dtb_fit);
#endif
if (gd->fdt_blob) {
struct lmb lmb;

View File

@ -880,6 +880,26 @@ If a parent has children these will be destroyed first. After this point
the device does not exist and its memory has be deallocated.
Special cases for removal
-------------------------
Some devices need to do clean-up before the OS is called. For example, a USB
driver may want to stop the bus. This can be done in the remove() method.
Some special flags are used to determine whether to remove the device:
DM_FLAG_OS_PREPARE - indicates that the device needs to get ready for OS
boot. The device will be removed just before the OS is booted
DM_REMOVE_ACTIVE_DMA - indicates that the device uses DMA. This is
effectively the same as DM_FLAG_OS_PREPARE, so the device is removed
before the OS is booted
DM_FLAG_VITAL - indicates that the device is 'vital' to the operation of
other devices. It is possible to remove this device after all regular
devices are removed. This is useful e.g. for a clock, which need to
be active during the device-removal phase.
The dm_remove_devices_flags() function can be used to remove devices based on
their driver flags.
Data Structures
---------------

View File

@ -8,6 +8,8 @@
* Pavel Herrmann <morpheus.ibis@gmail.com>
*/
#define LOG_CATEGORY LOGC_DM
#include <common.h>
#include <errno.h>
#include <log.h>
@ -45,20 +47,24 @@ int device_chld_remove(struct udevice *dev, struct driver *drv,
uint flags)
{
struct udevice *pos, *n;
int ret;
int result = 0;
assert(dev);
list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
int ret;
if (drv && (pos->driver != drv))
continue;
ret = device_remove(pos, flags);
if (ret)
if (ret == -EPROBE_DEFER)
result = ret;
else if (ret && ret != -EKEYREJECTED)
return ret;
}
return 0;
return result;
}
int device_unbind(struct udevice *dev)
@ -149,13 +155,43 @@ void device_free(struct udevice *dev)
devres_release_probe(dev);
}
static bool flags_remove(uint flags, uint drv_flags)
/**
* flags_remove() - Figure out whether to remove a device
*
* If this is called with @flags == DM_REMOVE_NON_VITAL | DM_REMOVE_ACTIVE_DMA,
* then it returns 0 (=go head and remove) if the device is not matked vital
* but is marked DM_REMOVE_ACTIVE_DMA.
*
* If this is called with @flags == DM_REMOVE_ACTIVE_DMA,
* then it returns 0 (=go head and remove) if the device is marked
* DM_REMOVE_ACTIVE_DMA, regardless of whether it is marked vital.
*
* @flags: Flags passed to device_remove()
* @drv_flags: Driver flags
* @return 0 if the device should be removed,
* -EKEYREJECTED if @flags includes a flag in DM_REMOVE_ACTIVE_ALL but
* @drv_flags does not (indicates that this device has nothing to do for
* DMA shutdown or OS prepare)
* -EPROBE_DEFER if @flags is DM_REMOVE_NON_VITAL but @drv_flags contains
* DM_FLAG_VITAL (indicates the device is vital and should not be removed)
*/
static int flags_remove(uint flags, uint drv_flags)
{
if ((flags & DM_REMOVE_NORMAL) ||
(flags && (drv_flags & (DM_FLAG_ACTIVE_DMA | DM_FLAG_OS_PREPARE))))
return true;
if (!(flags & DM_REMOVE_NORMAL)) {
bool vital_match;
bool active_match;
return false;
active_match = !(flags & DM_REMOVE_ACTIVE_ALL) ||
(drv_flags & flags);
vital_match = !(flags & DM_REMOVE_NON_VITAL) ||
!(drv_flags & DM_FLAG_VITAL);
if (!vital_match)
return -EPROBE_DEFER;
if (!active_match)
return -EKEYREJECTED;
}
return 0;
}
int device_remove(struct udevice *dev, uint flags)
@ -169,22 +205,32 @@ int device_remove(struct udevice *dev, uint flags)
if (!(dev_get_flags(dev) & DM_FLAG_ACTIVATED))
return 0;
drv = dev->driver;
assert(drv);
ret = uclass_pre_remove_device(dev);
if (ret)
return ret;
/*
* If the child returns EKEYREJECTED, continue. It just means that it
* didn't match the flags.
*/
ret = device_chld_remove(dev, NULL, flags);
if (ret)
goto err;
if (ret && ret != -EKEYREJECTED)
return ret;
/*
* Remove the device if called with the "normal" remove flag set,
* or if the remove flag matches any of the drivers remove flags
*/
if (drv->remove && flags_remove(flags, drv->flags)) {
drv = dev->driver;
assert(drv);
ret = flags_remove(flags, drv->flags);
if (ret) {
log_debug("%s: When removing: flags=%x, drv->flags=%x, err=%d\n",
dev->name, flags, drv->flags, ret);
return ret;
}
ret = uclass_pre_remove_device(dev);
if (ret)
return ret;
if (drv->remove) {
ret = drv->remove(dev);
if (ret)
goto err_remove;
@ -200,28 +246,20 @@ int device_remove(struct udevice *dev, uint flags)
if (!(flags & DM_REMOVE_NO_PD) &&
!(drv->flags &
(DM_FLAG_DEFAULT_PD_CTRL_OFF | DM_FLAG_REMOVE_WITH_PD_ON)) &&
(DM_FLAG_DEFAULT_PD_CTRL_OFF | DM_FLAG_LEAVE_PD_ON)) &&
dev != gd->cur_serial_dev)
dev_power_domain_off(dev);
if (flags_remove(flags, drv->flags)) {
device_free(dev);
device_free(dev);
dev_bic_flags(dev, DM_FLAG_ACTIVATED);
}
dev_bic_flags(dev, DM_FLAG_ACTIVATED);
return ret;
return 0;
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;
}

View File

@ -162,6 +162,8 @@ int dm_init(bool of_live)
int dm_uninit(void)
{
/* Remove non-vital devices first */
device_remove(dm_root(), DM_REMOVE_NON_VITAL);
device_remove(dm_root(), DM_REMOVE_NORMAL);
device_unbind(dm_root());
gd->dm_root = NULL;

View File

@ -50,6 +50,9 @@ static int sandbox_scmi_devices_remove(struct udevice *dev)
int ret = 0;
size_t n;
if (!devices)
return 0;
for (n = 0; n < SCMI_TEST_DEVICES_RD_COUNT; n++) {
int ret2 = reset_free(devices->reset + n);

View File

@ -2526,10 +2526,7 @@ int brcmnand_probe(struct udevice *dev, struct brcmnand_soc *soc)
if (ret)
return ret;
} else {
ret = PTR_ERR(ctrl->clk);
if (ret == -EPROBE_DEFER)
return ret;
/* Ignore PTR_ERR(ctrl->clk) */
ctrl->clk = NULL;
}

View File

@ -35,7 +35,7 @@ struct ftmac100_data {
*/
static void ftmac100_reset(struct ftmac100_data *priv)
{
struct ftmac100 *ftmac100 = (struct ftmac100 *)priv->iobase;
struct ftmac100 *ftmac100 = (struct ftmac100 *)(uintptr_t)priv->iobase;
debug ("%s()\n", __func__);
@ -56,7 +56,7 @@ static void ftmac100_reset(struct ftmac100_data *priv)
static void ftmac100_set_mac(struct ftmac100_data *priv ,
const unsigned char *mac)
{
struct ftmac100 *ftmac100 = (struct ftmac100 *)priv->iobase;
struct ftmac100 *ftmac100 = (struct ftmac100 *)(uintptr_t)priv->iobase;
unsigned int maddr = mac[0] << 8 | mac[1];
unsigned int laddr = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5];
@ -71,7 +71,7 @@ static void ftmac100_set_mac(struct ftmac100_data *priv ,
*/
static void _ftmac100_halt(struct ftmac100_data *priv)
{
struct ftmac100 *ftmac100 = (struct ftmac100 *)priv->iobase;
struct ftmac100 *ftmac100 = (struct ftmac100 *)(uintptr_t)priv->iobase;
debug ("%s()\n", __func__);
writel (0, &ftmac100->maccr);
}
@ -81,7 +81,7 @@ static void _ftmac100_halt(struct ftmac100_data *priv)
*/
static int _ftmac100_init(struct ftmac100_data *priv, unsigned char enetaddr[6])
{
struct ftmac100 *ftmac100 = (struct ftmac100 *)priv->iobase;
struct ftmac100 *ftmac100 = (struct ftmac100 *)(uintptr_t)priv->iobase;
struct ftmac100_txdes *txdes = priv->txdes;
struct ftmac100_rxdes *rxdes = priv->rxdes;
unsigned int maccr;
@ -186,7 +186,7 @@ static int __ftmac100_recv(struct ftmac100_data *priv)
*/
static int _ftmac100_send(struct ftmac100_data *priv, void *packet, int length)
{
struct ftmac100 *ftmac100 = (struct ftmac100 *)priv->iobase;
struct ftmac100 *ftmac100 = (struct ftmac100 *)(uintptr_t)priv->iobase;
struct ftmac100_txdes *curr_des = priv->txdes;
ulong start;

View File

@ -1283,5 +1283,5 @@ U_BOOT_DRIVER(pci_iproc) = {
.probe = iproc_pcie_probe,
.remove = iproc_pcie_remove,
.priv_auto = sizeof(struct iproc_pcie),
.flags = DM_REMOVE_OS_PREPARE,
.flags = DM_FLAG_OS_PREPARE,
};

View File

@ -178,7 +178,7 @@ static int sifive_serial_of_to_plat(struct udevice *dev)
{
struct sifive_uart_plat *plat = dev_get_plat(dev);
plat->regs = (struct uart_sifive *)dev_read_addr(dev);
plat->regs = (struct uart_sifive *)(uintptr_t)dev_read_addr(dev);
if (IS_ERR(plat->regs))
return PTR_ERR(plat->regs);

View File

@ -437,7 +437,7 @@ static int qcom_smem_alloc(unsigned int host, unsigned int item, size_t size)
int ret;
if (!__smem)
return -EPROBE_DEFER;
return -ENOMEM;
if (item < SMEM_ITEM_LAST_FIXED) {
dev_err(__smem->dev,
@ -559,7 +559,7 @@ static void *qcom_smem_get(unsigned int host, unsigned int item, size_t *size)
{
struct smem_partition_header *phdr;
size_t cacheln;
void *ptr = ERR_PTR(-EPROBE_DEFER);
void *ptr = ERR_PTR(-ENOMEM);
if (!__smem)
return ptr;
@ -597,7 +597,7 @@ static int qcom_smem_get_free_space(unsigned int host)
unsigned int ret;
if (!__smem)
return -EPROBE_DEFER;
return -ENOMEM;
if (host < SMEM_HOST_COUNT && __smem->partitions[host]) {
phdr = __smem->partitions[host];

View File

@ -212,5 +212,5 @@ U_BOOT_DRIVER(meson_vpu) = {
.probe = meson_vpu_probe,
.bind = meson_vpu_bind,
.priv_auto = sizeof(struct meson_vpu_priv),
.flags = DM_FLAG_PRE_RELOC | DM_FLAG_REMOVE_WITH_PD_ON,
.flags = DM_FLAG_PRE_RELOC | DM_FLAG_LEAVE_PD_ON,
};

View File

@ -119,5 +119,5 @@ U_BOOT_DRIVER(rti_wdt) = {
.ops = &rti_wdt_ops,
.probe = rti_wdt_probe,
.priv_auto = sizeof(struct rti_wdt_priv),
.flags = DM_FLAG_REMOVE_WITH_PD_ON,
.flags = DM_FLAG_LEAVE_PD_ON,
};

View File

@ -147,7 +147,7 @@ struct global_data {
/**
* @ram_top: top address of RAM used by U-Boot
*/
unsigned long ram_top;
phys_addr_t ram_top;
/**
* @relocaddr: start address of U-Boot in RAM
*

View File

@ -123,7 +123,9 @@ int device_probe(struct udevice *dev);
*
* @dev: Pointer to device to remove
* @flags: Flags for selective device removal (DM_REMOVE_...)
* @return 0 if OK, -ve on error (an error here is normally a very bad thing)
* @return 0 if OK, -EKEYREJECTED if not removed due to flags, -EPROBE_DEFER if
* this is a vital device and flags is DM_REMOVE_NON_VITAL, other -ve on
* error (such an error here is normally a very bad thing)
*/
#if CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)
int device_remove(struct udevice *dev, uint flags);
@ -173,10 +175,19 @@ static inline int device_chld_unbind(struct udevice *dev, struct driver *drv)
/**
* device_chld_remove() - Stop all device's children
*
* This continues through all children recursively stopping part-way through if
* an error occurs. Return values of -EKEYREJECTED are ignored and processing
* continues, since they just indicate that the child did not elect to be
* removed based on the value of @flags. Return values of -EPROBE_DEFER cause
* processing of other children to continue, but the function will return
* -EPROBE_DEFER.
*
* @dev: The device whose children are to be removed
* @drv: The targeted driver
* @flags: Flag, if this functions is called in the pre-OS stage
* @return 0 on success, -ve on error
* @return 0 on success, -EPROBE_DEFER if any child failed to remove, other
* -ve on error
*/
#if CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)
int device_chld_remove(struct udevice *dev, struct driver *drv,

View File

@ -71,12 +71,22 @@ struct driver_info;
* Device is removed without switching off its power domain. This might
* be required, i. e. for serial console (debug) output when booting OS.
*/
#define DM_FLAG_REMOVE_WITH_PD_ON (1 << 13)
#define DM_FLAG_LEAVE_PD_ON (1 << 13)
/*
* Device is vital to the operation of other devices. It is possible to remove
* removed this device after all regular devices are removed. This is useful
* e.g. for clock, which need to be active during the device-removal phase.
*/
#define DM_FLAG_VITAL (1 << 14)
/*
* One or multiple of these flags are passed to device_remove() so that
* a selective device removal as specified by the remove-stage and the
* driver flags can be done.
*
* DO NOT use these flags in your driver's @flags value...
* use the above DM_FLAG_... values instead
*/
enum {
/* Normal remove, remove all devices */
@ -88,7 +98,8 @@ enum {
/* Remove devices which need some final OS preparation steps */
DM_REMOVE_OS_PREPARE = DM_FLAG_OS_PREPARE,
/* Add more use cases here */
/* Remove only devices that are not marked vital */
DM_REMOVE_NON_VITAL = DM_FLAG_VITAL,
/* Remove devices with any active flag */
DM_REMOVE_ACTIVE_ALL = DM_REMOVE_ACTIVE_DMA | DM_REMOVE_OS_PREPARE,

View File

@ -326,7 +326,8 @@ void relocate_code(ulong start_addr_sp, struct global_data *new_gd,
#endif
/* Print a numeric value (for use in arch_print_bdinfo()) */
void bdinfo_print_num(const char *name, ulong value);
void bdinfo_print_num_l(const char *name, ulong value);
void bdinfo_print_num_ll(const char *name, unsigned long long value);
/* Print a clock speed in MHz */
void bdinfo_print_mhz(const char *name, unsigned long hz);

View File

@ -341,7 +341,7 @@ void os_localtime(struct rtc_time *rt);
/**
* os_abort() - raise SIGABRT to exit sandbox (e.g. to debugger)
*/
void os_abort(void);
void os_abort(void) __attribute__((noreturn));
/**
* os_mprotect_allow() - Remove write-protection on a region of memory

View File

@ -1572,7 +1572,7 @@ int fdtdec_setup(void)
return -1;
}
# elif defined(CONFIG_OF_PRIOR_STAGE)
gd->fdt_blob = (void *)prior_stage_fdt_address;
gd->fdt_blob = (void *)(uintptr_t)prior_stage_fdt_address;
# endif
# ifndef CONFIG_SPL_BUILD
/* Allow the early environment to override the fdt address */

View File

@ -72,6 +72,14 @@ static struct driver_info driver_info_act_dma = {
.name = "test_act_dma_drv",
};
static struct driver_info driver_info_vital_clk = {
.name = "test_vital_clk_drv",
};
static struct driver_info driver_info_act_dma_vital_clk = {
.name = "test_act_dma_vital_clk_drv",
};
void dm_leak_check_start(struct unit_test_state *uts)
{
uts->start = mallinfo();
@ -883,6 +891,92 @@ static int dm_test_remove_active_dma(struct unit_test_state *uts)
}
DM_TEST(dm_test_remove_active_dma, 0);
/* Test removal of 'vital' devices */
static int dm_test_remove_vital(struct unit_test_state *uts)
{
struct dm_test_state *dms = uts->priv;
struct udevice *normal, *dma, *vital, *dma_vital;
/* Skip the behaviour in test_post_probe() */
dms->skip_post_probe = 1;
ut_assertok(device_bind_by_name(dms->root, false, &driver_info_manual,
&normal));
ut_assertnonnull(normal);
ut_assertok(device_bind_by_name(dms->root, false, &driver_info_act_dma,
&dma));
ut_assertnonnull(dma);
ut_assertok(device_bind_by_name(dms->root, false,
&driver_info_vital_clk, &vital));
ut_assertnonnull(vital);
ut_assertok(device_bind_by_name(dms->root, false,
&driver_info_act_dma_vital_clk,
&dma_vital));
ut_assertnonnull(dma_vital);
/* Probe the devices */
ut_assertok(device_probe(normal));
ut_assertok(device_probe(dma));
ut_assertok(device_probe(vital));
ut_assertok(device_probe(dma_vital));
/* Check that devices are active right now */
ut_asserteq(true, device_active(normal));
ut_asserteq(true, device_active(dma));
ut_asserteq(true, device_active(vital));
ut_asserteq(true, device_active(dma_vital));
/* Remove active devices via selective remove flag */
dm_remove_devices_flags(DM_REMOVE_NON_VITAL | DM_REMOVE_ACTIVE_ALL);
/*
* Check that this only has an effect on the dma device, since two
* devices are vital and the third does not have active DMA
*/
ut_asserteq(true, device_active(normal));
ut_asserteq(false, device_active(dma));
ut_asserteq(true, device_active(vital));
ut_asserteq(true, device_active(dma_vital));
/* Remove active devices via selective remove flag */
ut_assertok(device_probe(dma));
dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL);
/* This should have affected both active-dma devices */
ut_asserteq(true, device_active(normal));
ut_asserteq(false, device_active(dma));
ut_asserteq(true, device_active(vital));
ut_asserteq(false, device_active(dma_vital));
/* Remove non-vital devices */
ut_assertok(device_probe(dma));
ut_assertok(device_probe(dma_vital));
dm_remove_devices_flags(DM_REMOVE_NON_VITAL);
/* This should have affected only non-vital devices */
ut_asserteq(false, device_active(normal));
ut_asserteq(false, device_active(dma));
ut_asserteq(true, device_active(vital));
ut_asserteq(true, device_active(dma_vital));
/* Remove vital devices via normal remove flag */
ut_assertok(device_probe(normal));
ut_assertok(device_probe(dma));
dm_remove_devices_flags(DM_REMOVE_NORMAL);
/* Check that all devices are inactive right now */
ut_asserteq(false, device_active(normal));
ut_asserteq(false, device_active(dma));
ut_asserteq(false, device_active(vital));
ut_asserteq(false, device_active(dma_vital));
return 0;
}
DM_TEST(dm_test_remove_vital, 0);
static int dm_test_uclass_before_ready(struct unit_test_state *uts)
{
struct uclass *uc;

View File

@ -170,3 +170,25 @@ U_BOOT_DRIVER(test_act_dma_drv) = {
.unbind = test_manual_unbind,
.flags = DM_FLAG_ACTIVE_DMA,
};
U_BOOT_DRIVER(test_vital_clk_drv) = {
.name = "test_vital_clk_drv",
.id = UCLASS_TEST,
.ops = &test_manual_ops,
.bind = test_manual_bind,
.probe = test_manual_probe,
.remove = test_manual_remove,
.unbind = test_manual_unbind,
.flags = DM_FLAG_VITAL,
};
U_BOOT_DRIVER(test_act_dma_vital_clk_drv) = {
.name = "test_act_dma_vital_clk_drv",
.id = UCLASS_TEST,
.ops = &test_manual_ops,
.bind = test_manual_bind,
.probe = test_manual_probe,
.remove = test_manual_remove,
.unbind = test_manual_unbind,
.flags = DM_FLAG_VITAL | DM_FLAG_ACTIVE_DMA,
};

View File

@ -123,7 +123,9 @@ static int dm_test_virtio_remove(struct unit_test_state *uts)
/* check the device can be successfully removed */
dev_or_flags(dev, DM_FLAG_ACTIVATED);
ut_assertok(device_remove(bus, DM_REMOVE_ACTIVE_ALL));
ut_asserteq(-EKEYREJECTED, device_remove(bus, DM_REMOVE_ACTIVE_ALL));
ut_asserteq(false, device_active(dev));
return 0;
}