diff options
Diffstat (limited to 'roms/u-boot/drivers/core')
22 files changed, 8368 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/core/Kconfig b/roms/u-boot/drivers/core/Kconfig new file mode 100644 index 000000000..d618e1637 --- /dev/null +++ b/roms/u-boot/drivers/core/Kconfig @@ -0,0 +1,345 @@ +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_SPL_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_SPL_SYS_MALLOC_F_LEN for more details on how to enable it. + +config TPL_DM + bool "Enable Driver Model for TPL" + depends on DM && TPL + help + Enable driver model in TPL. 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_SPL_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 enough. See + CONFIG_SPL_SYS_MALLOC_F_LEN for more details on how to enable it. + Disable this for very small implementations. + +config DM_WARN + bool "Enable warnings in driver model" + depends on DM + default y + help + Enable this to see warnings related to driver model. + + Warnings may help with debugging, such as when expected devices do + not bind correctly. If the option is disabled, dm_warn() is compiled + out - it will do nothing when called. + +config SPL_DM_WARN + bool "Enable warnings in driver model wuth SPL" + depends on SPL_DM + help + Enable this to see warnings related to driver model in SPL + + 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. + + Warnings may help with debugging, such as when expected devices do + not bind correctly. If the option is disabled, dm_warn() is compiled + out - it will do nothing when called. + +config DM_DEBUG + bool "Enable debug messages in driver model core" + depends on DM + help + Say Y here if you want to compile in debug messages in DM core. + +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. + + 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 SPL_DM_DEVICE_REMOVE + bool "Support device removal in SPL" + depends on SPL_DM + default n + 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. + +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 SPL_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 SPL_DM_INLINE_OFNODE + bool "Inline some ofnode functions which are seldom used in SPL" + depends on SPL_DM + default y + help + This applies to several ofnode functions (see ofnode.h) which are + seldom used. Inlining them can help reduce code size. + +config TPL_DM_INLINE_OFNODE + bool "Inline some ofnode functions which are seldom used in TPL" + depends on TPL_DM + default y + help + This applies to several ofnode functions (see ofnode.h) which are + seldom used. Inlining them can help reduce code size. + +config DM_DMA + bool "Support per-device DMA constraints" + depends on DM + default n + help + Enable this to extract per-device DMA constraints, only supported on + device-tree systems for now. This is needed in order translate + addresses on systems where different buses have different views of + the physical address space. + +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 SPL_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 TPL_REGMAP + bool "Support register maps in TPL" + depends on TPL_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 SPL_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 TPL_SYSCON + bool "Support system controllers in TPL" + depends on TPL_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 SIMPLE_BUS_CORRECT_RANGE + bool "Decode the 'simple-bus' <range> by honoring the #address-cells and #size-cells" + depends on SIMPLE_BUS + default y if SANDBOX + help + Decoding the 'simple-bus' <range> by honoring the #address-cells + and #size-cells of parent/child bus. If unset, #address-cells of + parent bus is assumed to be 1, #address-cells and #size-cells of + child bus is also assumed to be 1, to save some spaces of using + an advanced API to decode the <range>, which benefits SPL image + builds that have size limits. + + If you are unsure about this, Say N here. + +config SIMPLE_PM_BUS + bool "Support simple-pm-bus driver" + depends on DM && OF_CONTROL && CLK && POWER_DOMAIN + help + Supports the 'simple-pm-bus' driver, which is used for busses that + have power domains and/or clocks which need to be enabled before use. + +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 TRANSLATION_OFFSET + bool "Platforms specific translation offset" + depends on DM && OF_CONTROL + help + Some platforms need a special address translation. Those + platforms (e.g. mvebu in SPL) can configure a translation + offset by enabling this option and setting the translation_offset + variable in the GD in their platform- / board-specific code. + +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 targeted, 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. + +config DM_DEV_READ_INLINE + bool + default y if !OF_LIVE + +config ACPIGEN + bool "Support ACPI table generation in driver model" + default y if SANDBOX || (GENERATE_ACPI_TABLE && !QEMU) + help + This option enables generation of ACPI tables using driver-model + devices. It adds a new operation struct to each driver, to support + things like generating device-specific tables and returning the ACPI + name of a device. + +config BOUNCE_BUFFER + bool "Include bounce buffer API" + help + Some peripherals support DMA from a subset of physically + addressable memory only. To support such peripherals, the + bounce buffer API uses a temporary buffer: it copies data + to/from DMA regions while managing cache operations. + + A second possible use of bounce buffers is their ability to + provide aligned buffers for DMA operations. + +endmenu diff --git a/roms/u-boot/drivers/core/Makefile b/roms/u-boot/drivers/core/Makefile new file mode 100644 index 000000000..5edd4e413 --- /dev/null +++ b/roms/u-boot/drivers/core/Makefile @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2013 Google, Inc + +obj-y += device.o fdtaddr.o lists.o root.o uclass.o util.o +obj-$(CONFIG_$(SPL_TPL_)ACPIGEN) += acpi.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_SIMPLE_PM_BUS) += simple-pm-bus.o +obj-$(CONFIG_DM) += dump.o +obj-$(CONFIG_$(SPL_TPL_)REGMAP) += regmap.o +obj-$(CONFIG_$(SPL_TPL_)SYSCON) += syscon-uclass.o +obj-$(CONFIG_$(SPL_)OF_LIVE) += of_access.o of_addr.o +ifndef CONFIG_DM_DEV_READ_INLINE +obj-$(CONFIG_OF_CONTROL) += read.o +endif +obj-$(CONFIG_OF_CONTROL) += of_extra.o ofnode.o read_extra.o + +ccflags-$(CONFIG_DM_DEBUG) += -DDEBUG diff --git a/roms/u-boot/drivers/core/acpi.c b/roms/u-boot/drivers/core/acpi.c new file mode 100644 index 000000000..2176d8b83 --- /dev/null +++ b/roms/u-boot/drivers/core/acpi.c @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Core driver model support for ACPI table generation + * + * Copyright 2019 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#define LOG_CATEOGRY LOGC_ACPI + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <acpi/acpi_device.h> +#include <dm/acpi.h> +#include <dm/device-internal.h> +#include <dm/root.h> + +#define MAX_ACPI_ITEMS 100 + +/* Type of table that we collected */ +enum gen_type_t { + TYPE_NONE, + TYPE_SSDT, + TYPE_DSDT, +}; + +/* Type of method to call */ +enum method_t { + METHOD_WRITE_TABLES, + METHOD_FILL_SSDT, + METHOD_INJECT_DSDT, + METHOD_SETUP_NHLT, +}; + +/* Prototype for all methods */ +typedef int (*acpi_method)(const struct udevice *dev, struct acpi_ctx *ctx); + +/** + * struct acpi_item - Holds info about ACPI data generated by a driver method + * + * @dev: Device that generated this data + * @type: Table type it refers to + * @buf: Buffer containing the data + * @size: Size of the data in bytes + */ +struct acpi_item { + struct udevice *dev; + enum gen_type_t type; + char *buf; + int size; +}; + +/* List of ACPI items collected */ +static struct acpi_item acpi_item[MAX_ACPI_ITEMS]; +static int item_count; + +int acpi_copy_name(char *out_name, const char *name) +{ + strncpy(out_name, name, ACPI_NAME_LEN); + out_name[ACPI_NAME_LEN] = '\0'; + + return 0; +} + +int acpi_get_name(const struct udevice *dev, char *out_name) +{ + struct acpi_ops *aops; + const char *name; + int ret; + + aops = device_get_acpi_ops(dev); + if (aops && aops->get_name) + return aops->get_name(dev, out_name); + name = dev_read_string(dev, "acpi,name"); + if (name) + return acpi_copy_name(out_name, name); + ret = acpi_device_infer_name(dev, out_name); + if (ret) + return log_msg_ret("dev", ret); + + return 0; +} + +int acpi_get_path(const struct udevice *dev, char *out_path, int maxlen) +{ + const char *path; + int ret; + + path = dev_read_string(dev, "acpi,path"); + if (path) { + if (strlen(path) >= maxlen) + return -ENOSPC; + strcpy(out_path, path); + return 0; + } + ret = acpi_device_path(dev, out_path, maxlen); + if (ret) + return log_msg_ret("dev", ret); + + return 0; +} + +/** + * acpi_add_item() - Add a new item to the list of data collected + * + * @ctx: ACPI context + * @dev: Device that generated the data + * @type: Table type it refers to + * @start: The start of the data (the end is obtained from ctx->current) + * @return 0 if OK, -ENOSPC if too many items, -ENOMEM if out of memory + */ +static int acpi_add_item(struct acpi_ctx *ctx, struct udevice *dev, + enum gen_type_t type, void *start) +{ + struct acpi_item *item; + void *end = ctx->current; + + if (item_count == MAX_ACPI_ITEMS) { + log_err("Too many items\n"); + return log_msg_ret("mem", -ENOSPC); + } + + item = &acpi_item[item_count]; + item->dev = dev; + item->type = type; + item->size = end - start; + if (!item->size) + return 0; + item->buf = malloc(item->size); + if (!item->buf) + return log_msg_ret("mem", -ENOMEM); + memcpy(item->buf, start, item->size); + item_count++; + log_debug("* %s: Added type %d, %p, size %x\n", dev->name, type, start, + item->size); + + return 0; +} + +void acpi_dump_items(enum acpi_dump_option option) +{ + int i; + + for (i = 0; i < item_count; i++) { + struct acpi_item *item = &acpi_item[i]; + + printf("dev '%s', type %d, size %x\n", item->dev->name, + item->type, item->size); + if (option == ACPI_DUMP_CONTENTS) { + print_buffer(0, item->buf, 1, item->size, 0); + printf("\n"); + } + } +} + +static struct acpi_item *find_acpi_item(const char *devname) +{ + int i; + + for (i = 0; i < item_count; i++) { + struct acpi_item *item = &acpi_item[i]; + + if (!strcmp(devname, item->dev->name)) + return item; + } + + return NULL; +} + +/** + * sort_acpi_item_type - Sort the ACPI items into the desired order + * + * This looks up the ordering in the device tree and then adds each item one by + * one into the supplied buffer + * + * @ctx: ACPI context + * @start: Start position to put the sorted items. The items will follow each + * other in sorted order + * @type: Type of items to sort + * @return 0 if OK, -ve on error + */ +static int sort_acpi_item_type(struct acpi_ctx *ctx, void *start, + enum gen_type_t type) +{ + const u32 *order; + int size; + int count; + void *ptr; + void *end = ctx->current; + + ptr = start; + order = ofnode_read_chosen_prop(type == TYPE_DSDT ? + "u-boot,acpi-dsdt-order" : + "u-boot,acpi-ssdt-order", &size); + if (!order) { + log_debug("Failed to find ordering, leaving as is\n"); + return 0; + } + + /* + * This algorithm rewrites the context buffer without changing its + * length. So there is no need to update ctx-current + */ + count = size / sizeof(u32); + while (count--) { + struct acpi_item *item; + const char *name; + ofnode node; + + node = ofnode_get_by_phandle(fdt32_to_cpu(*order++)); + name = ofnode_get_name(node); + item = find_acpi_item(name); + if (!item) { + log_err("Failed to find item '%s'\n", name); + return log_msg_ret("find", -ENOENT); + } + if (item->type == type) { + log_debug(" - add %s\n", item->dev->name); + memcpy(ptr, item->buf, item->size); + ptr += item->size; + } + } + + /* + * If the sort order is missing an item then the output will be too + * small. Report this error since the item needs to be added to the + * ordering for the ACPI tables to be complete. + */ + if (ptr != end) { + log_warning("*** Missing bytes: ptr=%p, end=%p\n", ptr, end); + return -ENXIO; + } + + return 0; +} + +acpi_method acpi_get_method(struct udevice *dev, enum method_t method) +{ + struct acpi_ops *aops; + + aops = device_get_acpi_ops(dev); + if (aops) { + switch (method) { + case METHOD_WRITE_TABLES: + return aops->write_tables; + case METHOD_FILL_SSDT: + return aops->fill_ssdt; + case METHOD_INJECT_DSDT: + return aops->inject_dsdt; + case METHOD_SETUP_NHLT: + return aops->setup_nhlt; + } + } + + return NULL; +} + +int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent, + enum method_t method, enum gen_type_t type) +{ + struct udevice *dev; + acpi_method func; + int ret; + + func = acpi_get_method(parent, method); + if (func) { + void *start = ctx->current; + + log_debug("- method %d, %s %p\n", method, parent->name, func); + ret = device_of_to_plat(parent); + if (ret) + return log_msg_ret("ofdata", ret); + ret = func(parent, ctx); + if (ret) + return log_msg_ret("func", ret); + + /* Add the item to the internal list */ + if (type != TYPE_NONE) { + ret = acpi_add_item(ctx, parent, type, start); + if (ret) + return log_msg_ret("add", ret); + } + } + device_foreach_child(dev, parent) { + ret = acpi_recurse_method(ctx, dev, method, type); + if (ret) + return log_msg_ret("recurse", ret); + } + + return 0; +} + +int acpi_fill_ssdt(struct acpi_ctx *ctx) +{ + void *start = ctx->current; + int ret; + + log_debug("Writing SSDT tables\n"); + ret = acpi_recurse_method(ctx, dm_root(), METHOD_FILL_SSDT, TYPE_SSDT); + log_debug("Writing SSDT finished, err=%d\n", ret); + ret = sort_acpi_item_type(ctx, start, TYPE_SSDT); + if (ret) + return log_msg_ret("build", ret); + + return ret; +} + +int acpi_inject_dsdt(struct acpi_ctx *ctx) +{ + void *start = ctx->current; + int ret; + + log_debug("Writing DSDT tables\n"); + ret = acpi_recurse_method(ctx, dm_root(), METHOD_INJECT_DSDT, + TYPE_DSDT); + log_debug("Writing DSDT finished, err=%d\n", ret); + ret = sort_acpi_item_type(ctx, start, TYPE_DSDT); + if (ret) + return log_msg_ret("build", ret); + + return ret; +} + +void acpi_reset_items(void) +{ + item_count = 0; +} + +int acpi_write_dev_tables(struct acpi_ctx *ctx) +{ + int ret; + + log_debug("Writing device tables\n"); + ret = acpi_recurse_method(ctx, dm_root(), METHOD_WRITE_TABLES, + TYPE_NONE); + log_debug("Writing finished, err=%d\n", ret); + + return ret; +} + +int acpi_setup_nhlt(struct acpi_ctx *ctx, struct nhlt *nhlt) +{ + int ret; + + log_debug("Setup NHLT\n"); + ctx->nhlt = nhlt; + ret = acpi_recurse_method(ctx, dm_root(), METHOD_SETUP_NHLT, TYPE_NONE); + log_debug("Setup finished, err=%d\n", ret); + + return ret; +} diff --git a/roms/u-boot/drivers/core/device-remove.c b/roms/u-boot/drivers/core/device-remove.c new file mode 100644 index 000000000..11d3959d2 --- /dev/null +++ b/roms/u-boot/drivers/core/device-remove.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Device manager + * + * Copyright (c) 2014 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + */ + +#define LOG_CATEGORY LOGC_DM + +#include <common.h> +#include <errno.h> +#include <log.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> +#include <power-domain.h> +#include <asm/global_data.h> + +int device_chld_unbind(struct udevice *dev, struct driver *drv) +{ + struct udevice *pos, *n; + int ret, saved_ret = 0; + + assert(dev); + + list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) { + if (drv && (pos->driver != drv)) + continue; + + ret = device_unbind(pos); + if (ret && !saved_ret) { + log_warning("device '%s' failed to unbind\n", + pos->name); + saved_ret = ret; + } + } + + return log_ret(saved_ret); +} + +int device_chld_remove(struct udevice *dev, struct driver *drv, + uint flags) +{ + struct udevice *pos, *n; + 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 == -EPROBE_DEFER) + result = ret; + else if (ret && ret != -EKEYREJECTED) + return ret; + } + + return result; +} + +int device_unbind(struct udevice *dev) +{ + const struct driver *drv; + int ret; + + if (!dev) + return log_msg_ret("dev", -EINVAL); + + if (dev_get_flags(dev) & DM_FLAG_ACTIVATED) + return log_msg_ret("active", -EINVAL); + + if (!(dev_get_flags(dev) & DM_FLAG_BOUND)) + return log_msg_ret("not-bound", -EINVAL); + + drv = dev->driver; + assert(drv); + + if (drv->unbind) { + ret = drv->unbind(dev); + if (ret) + return log_msg_ret("unbind", ret); + } + + ret = device_chld_unbind(dev, NULL); + if (ret) + return log_msg_ret("child unbind", ret); + + if (dev_get_flags(dev) & DM_FLAG_ALLOC_PDATA) { + free(dev_get_plat(dev)); + dev_set_plat(dev, NULL); + } + if (dev_get_flags(dev) & DM_FLAG_ALLOC_UCLASS_PDATA) { + free(dev_get_uclass_plat(dev)); + dev_set_uclass_plat(dev, NULL); + } + if (dev_get_flags(dev) & DM_FLAG_ALLOC_PARENT_PDATA) { + free(dev_get_parent_plat(dev)); + dev_set_parent_plat(dev, NULL); + } + ret = uclass_unbind_device(dev); + if (ret) + return log_msg_ret("uc", ret); + + if (dev->parent) + list_del(&dev->sibling_node); + + devres_release_all(dev); + + if (dev_get_flags(dev) & DM_FLAG_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) { + free(dev_get_priv(dev)); + dev_set_priv(dev, NULL); + } + size = dev->uclass->uc_drv->per_device_auto; + if (size) { + free(dev_get_uclass_priv(dev)); + dev_set_uclass_priv(dev, NULL); + } + if (dev->parent) { + size = dev->parent->driver->per_child_auto; + if (!size) { + size = dev->parent->uclass->uc_drv-> + per_child_auto; + } + if (size) { + free(dev_get_parent_priv(dev)); + dev_set_parent_priv(dev, NULL); + } + } + dev_bic_flags(dev, DM_FLAG_PLATDATA_VALID); + + devres_release_probe(dev); +} + +/** + * 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)) { + bool vital_match; + bool active_match; + + 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) +{ + const struct driver *drv; + int ret; + + if (!dev) + return -EINVAL; + + if (!(dev_get_flags(dev) & DM_FLAG_ACTIVATED)) + return 0; + + /* + * 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 && 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 + */ + 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; + } + + 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); + } + } + + if (!(flags & DM_REMOVE_NO_PD) && + !(drv->flags & + (DM_FLAG_DEFAULT_PD_CTRL_OFF | DM_FLAG_LEAVE_PD_ON)) && + dev != gd->cur_serial_dev) + dev_power_domain_off(dev); + + device_free(dev); + + dev_bic_flags(dev, DM_FLAG_ACTIVATED); + + 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); + + return ret; +} diff --git a/roms/u-boot/drivers/core/device.c b/roms/u-boot/drivers/core/device.c new file mode 100644 index 000000000..cb960f8ec --- /dev/null +++ b/roms/u-boot/drivers/core/device.c @@ -0,0 +1,1171 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Device manager + * + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + */ + +#include <common.h> +#include <cpu_func.h> +#include <log.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <clk.h> +#include <fdtdec.h> +#include <fdt_support.h> +#include <malloc.h> +#include <asm/cache.h> +#include <dm/device.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/of_access.h> +#include <dm/pinctrl.h> +#include <dm/platdata.h> +#include <dm/read.h> +#include <dm/uclass.h> +#include <dm/uclass-internal.h> +#include <dm/util.h> +#include <linux/err.h> +#include <linux/list.h> +#include <power-domain.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int device_bind_common(struct udevice *parent, const struct driver *drv, + const char *name, void *plat, + ulong driver_data, ofnode node, + uint of_plat_size, struct udevice **devp) +{ + struct udevice *dev; + struct uclass *uc; + int size, ret = 0; + bool auto_seq = true; + void *ptr; + + if (CONFIG_IS_ENABLED(OF_PLATDATA_NO_BIND)) + return -ENOSYS; + + 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_set_plat(dev, plat); + dev->driver_data = driver_data; + dev->name = name; + dev_set_ofnode(dev, node); + dev->parent = parent; + dev->driver = drv; + dev->uclass = uc; + + dev->seq_ = -1; + if (CONFIG_IS_ENABLED(DM_SEQ_ALIAS) && + (uc->uc_drv->flags & DM_UC_FLAG_SEQ_ALIAS)) { + /* + * Some devices, such as a SPI bus, I2C bus and serial ports + * are numbered using aliases. + */ + if (CONFIG_IS_ENABLED(OF_CONTROL) && + !CONFIG_IS_ENABLED(OF_PLATDATA)) { + if (uc->uc_drv->name && ofnode_valid(node)) { + if (!dev_read_alias_seq(dev, &dev->seq_)) + auto_seq = false; + } + } + } + if (auto_seq && !(uc->uc_drv->flags & DM_UC_FLAG_NO_AUTO_SEQ)) + dev->seq_ = uclass_find_next_free_seq(uc); + + /* Check if we need to allocate plat */ + if (drv->plat_auto) { + bool alloc = !plat; + + /* + * For of-platdata, we try use the existing data, but if + * plat_auto is larger, we must allocate a new space + */ + if (CONFIG_IS_ENABLED(OF_PLATDATA)) { + if (of_plat_size) + dev_or_flags(dev, DM_FLAG_OF_PLATDATA); + if (of_plat_size < drv->plat_auto) + alloc = true; + } + if (alloc) { + dev_or_flags(dev, DM_FLAG_ALLOC_PDATA); + ptr = calloc(1, drv->plat_auto); + if (!ptr) { + ret = -ENOMEM; + goto fail_alloc1; + } + + /* + * For of-platdata, copy the old plat into the new + * space + */ + if (CONFIG_IS_ENABLED(OF_PLATDATA) && plat) + memcpy(ptr, plat, of_plat_size); + dev_set_plat(dev, ptr); + } + } + + size = uc->uc_drv->per_device_plat_auto; + if (size) { + dev_or_flags(dev, DM_FLAG_ALLOC_UCLASS_PDATA); + ptr = calloc(1, size); + if (!ptr) { + ret = -ENOMEM; + goto fail_alloc2; + } + dev_set_uclass_plat(dev, ptr); + } + + if (parent) { + size = parent->driver->per_child_plat_auto; + if (!size) + size = parent->uclass->uc_drv->per_child_plat_auto; + if (size) { + dev_or_flags(dev, DM_FLAG_ALLOC_PARENT_PDATA); + ptr = calloc(1, size); + if (!ptr) { + ret = -ENOMEM; + goto fail_alloc3; + } + dev_set_parent_plat(dev, ptr); + } + /* put dev into parent's successor list */ + 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) + pr_debug("Bound device %s to %s\n", dev->name, parent->name); + if (devp) + *devp = dev; + + dev_or_flags(dev, 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_get_flags(dev) & DM_FLAG_ALLOC_PARENT_PDATA) { + free(dev_get_parent_plat(dev)); + dev_set_parent_plat(dev, NULL); + } + } +fail_alloc3: + 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); + } + } +fail_alloc2: + 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); + } + } +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, ofnode node, + struct udevice **devp) +{ + return device_bind_common(parent, drv, name, NULL, driver_data, node, + 0, devp); +} + +int device_bind(struct udevice *parent, const struct driver *drv, + const char *name, void *plat, ofnode node, + struct udevice **devp) +{ + return device_bind_common(parent, drv, name, plat, 0, node, 0, + devp); +} + +int device_bind_by_name(struct udevice *parent, bool pre_reloc_only, + const struct driver_info *info, struct udevice **devp) +{ + struct driver *drv; + uint plat_size = 0; + int ret; + + drv = lists_driver_lookup_name(info->name); + if (!drv) + return -ENOENT; + if (pre_reloc_only && !(drv->flags & DM_FLAG_PRE_RELOC)) + return -EPERM; + +#if CONFIG_IS_ENABLED(OF_PLATDATA) + plat_size = info->plat_size; +#endif + ret = device_bind_common(parent, drv, info->name, (void *)info->plat, 0, + ofnode_null(), plat_size, devp); + if (ret) + return ret; + + return ret; +} + +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; +} + +static void *alloc_priv(int size, uint flags) +{ + void *priv; + + if (flags & DM_FLAG_ALLOC_PRIV_DMA) { + size = ROUND(size, ARCH_DMA_MINALIGN); + priv = memalign(ARCH_DMA_MINALIGN, size); + if (priv) { + memset(priv, '\0', size); + + /* + * 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 + } + } else { + priv = calloc(1, size); + } + + return priv; +} + +/** + * device_alloc_priv() - Allocate priv/plat data required by the device + * + * @dev: Device to process + * @return 0 if OK, -ENOMEM if out of memory + */ +static int device_alloc_priv(struct udevice *dev) +{ + const struct driver *drv; + void *ptr; + int size; + + drv = dev->driver; + assert(drv); + + /* Allocate private data if requested and not reentered */ + if (drv->priv_auto && !dev_get_priv(dev)) { + ptr = alloc_priv(drv->priv_auto, drv->flags); + if (!ptr) + return -ENOMEM; + dev_set_priv(dev, ptr); + } + + /* Allocate private data if requested and not reentered */ + size = dev->uclass->uc_drv->per_device_auto; + if (size && !dev_get_uclass_priv(dev)) { + ptr = alloc_priv(size, dev->uclass->uc_drv->flags); + if (!ptr) + return -ENOMEM; + dev_set_uclass_priv(dev, ptr); + } + + /* Allocate parent data for this child */ + if (dev->parent) { + size = dev->parent->driver->per_child_auto; + if (!size) + size = dev->parent->uclass->uc_drv->per_child_auto; + if (size && !dev_get_parent_priv(dev)) { + ptr = alloc_priv(size, drv->flags); + if (!ptr) + return -ENOMEM; + dev_set_parent_priv(dev, ptr); + } + } + + return 0; +} + +int device_of_to_plat(struct udevice *dev) +{ + const struct driver *drv; + int ret; + + if (!dev) + return -EINVAL; + + if (dev_get_flags(dev) & DM_FLAG_PLATDATA_VALID) + return 0; + + /* + * 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); + if (ret) + goto fail; + } + drv = dev->driver; + assert(drv); + + if (drv->of_to_plat && + (CONFIG_IS_ENABLED(OF_PLATDATA) || dev_has_ofnode(dev))) { + ret = drv->of_to_plat(dev); + if (ret) + goto fail; + } + + dev_or_flags(dev, DM_FLAG_PLATDATA_VALID); + + return 0; +fail: + device_free(dev); + + return ret; +} + +/** + * 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; +} + +int device_probe(struct udevice *dev) +{ + const struct driver *drv; + int ret; + + if (!dev) + return -EINVAL; + + if (dev_get_flags(dev) & DM_FLAG_ACTIVATED) + return 0; + + drv = dev->driver; + assert(drv); + + ret = device_of_to_plat(dev); + if (ret) + goto fail; + + /* Ensure all parents are probed */ + if (dev->parent) { + 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_get_flags(dev) & DM_FLAG_ACTIVATED) + return 0; + } + + dev_or_flags(dev, 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. + * + * 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. + */ + if (dev->parent && device_get_uclass_id(dev) != UCLASS_PINCTRL) + pinctrl_select_state(dev, "default"); + + 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; + } + + ret = device_get_dma_constraints(dev); + if (ret) + goto fail; + + 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; + } + + /* Only handle devices that have a valid ofnode */ + if (dev_has_ofnode(dev)) { + /* + * Process 'assigned-{clocks/clock-parents/clock-rates}' + * properties + */ + ret = clk_set_defaults(dev, 0); + if (ret) + goto fail; + } + + if (drv->probe) { + ret = drv->probe(dev); + if (ret) + 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_REMOVE_NORMAL)) { + dm_warn("%s: Device '%s' failed to remove on error path\n", + __func__, dev->name); + } +fail: + dev_bic_flags(dev, DM_FLAG_ACTIVATED); + + device_free(dev); + + return ret; +} + +void *dev_get_plat(const struct udevice *dev) +{ + if (!dev) { + dm_warn("%s: null device\n", __func__); + return NULL; + } + + return dm_priv_to_rw(dev->plat_); +} + +void *dev_get_parent_plat(const struct udevice *dev) +{ + if (!dev) { + dm_warn("%s: null device\n", __func__); + return NULL; + } + + return dm_priv_to_rw(dev->parent_plat_); +} + +void *dev_get_uclass_plat(const struct udevice *dev) +{ + if (!dev) { + dm_warn("%s: null device\n", __func__); + return NULL; + } + + return dm_priv_to_rw(dev->uclass_plat_); +} + +void *dev_get_priv(const struct udevice *dev) +{ + if (!dev) { + dm_warn("%s: null device\n", __func__); + return NULL; + } + + return dm_priv_to_rw(dev->priv_); +} + +void *dev_get_uclass_priv(const struct udevice *dev) +{ + if (!dev) { + dm_warn("%s: null device\n", __func__); + return NULL; + } + + return dm_priv_to_rw(dev->uclass_priv_); +} + +void *dev_get_parent_priv(const struct udevice *dev) +{ + if (!dev) { + dm_warn("%s: null device\n", __func__); + return NULL; + } + + return dm_priv_to_rw(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; +} + +#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) +/** + * 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; + + list_for_each_entry(uc, gd->uclass_root, sibling_node) { + ret = uclass_find_device_by_ofnode(uc->uc_drv->id, node, + &dev); + if (!ret || dev) { + *devp = dev; + return 0; + } + } + + return -ENODEV; +} +#endif + +int device_get_child(const 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_get_child_count(const struct udevice *parent) +{ + struct udevice *dev; + int count = 0; + + list_for_each_entry(dev, &parent->child_head, sibling_node) + count++; + + return count; +} + +int device_find_child_by_seq(const struct udevice *parent, int seq, + struct udevice **devp) +{ + struct udevice *dev; + + *devp = NULL; + + list_for_each_entry(dev, &parent->child_head, sibling_node) { + if (dev->seq_ == seq) { + *devp = dev; + return 0; + } + } + + return -ENODEV; +} + +int device_get_child_by_seq(const struct udevice *parent, int seq, + struct udevice **devp) +{ + struct udevice *dev; + int ret; + + *devp = NULL; + ret = device_find_child_by_seq(parent, seq, &dev); + + return device_get_device_tail(dev, ret, devp); +} + +int device_find_child_by_of_offset(const 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(dev) == of_offset) { + *devp = dev; + return 0; + } + } + + return -ENODEV; +} + +int device_get_child_by_of_offset(const 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_ofnode(struct udevice *parent, + ofnode ofnode) +{ + struct udevice *dev, *found; + + if (ofnode_equal(dev_ofnode(parent), ofnode)) + return parent; + + list_for_each_entry(dev, &parent->child_head, sibling_node) { + found = _device_find_global_by_ofnode(dev, ofnode); + if (found) + return found; + } + + return NULL; +} + +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) +{ + struct udevice *dev; + + dev = _device_find_global_by_ofnode(gd->dm_root, ofnode); + return device_get_device_tail(dev, dev ? 0 : -ENOENT, devp); +} + +#if CONFIG_IS_ENABLED(OF_PLATDATA) +int device_get_by_ofplat_idx(uint idx, struct udevice **devp) +{ + struct udevice *dev; + + 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; + } + *devp = NULL; + + return device_get_device_tail(dev, dev ? 0 : -ENOENT, devp); +} +#endif + +int device_find_first_child(const 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; +} + +int device_find_first_inactive_child(const struct udevice *parent, + 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; +} + +int device_find_first_child_by_uclass(const struct udevice *parent, + 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; +} + +int device_find_child_by_name(const struct udevice *parent, const char *name, + struct udevice **devp) +{ + struct udevice *dev; + + *devp = NULL; + + list_for_each_entry(dev, &parent->child_head, sibling_node) { + if (!strcmp(dev->name, name)) { + *devp = dev; + return 0; + } + } + + return -ENODEV; +} + +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); +} + +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; + + ret = device_of_to_plat(dev); + 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; + + ret = device_of_to_plat(dev); + if (ret) + return ret; + + *devp = dev; + + return 0; +} + +struct udevice *dev_get_parent(const struct udevice *child) +{ + return child->parent; +} + +ulong dev_get_driver_data(const struct udevice *dev) +{ + return dev->driver_data; +} + +const void *dev_get_driver_ops(const struct udevice *dev) +{ + if (!dev || !dev->driver->ops) + return NULL; + + return dev->driver->ops; +} + +enum uclass_id device_get_uclass_id(const struct udevice *dev) +{ + return dev->uclass->uc_drv->id; +} + +const char *dev_get_uclass_name(const struct udevice *dev) +{ + if (!dev) + return NULL; + + return dev->uclass->uc_drv->name; +} + +bool device_has_children(const struct udevice *dev) +{ + return !list_empty(&dev->child_head); +} + +bool device_has_active_children(const 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(const 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_or_flags(dev, DM_FLAG_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; +} + +void dev_set_priv(struct udevice *dev, void *priv) +{ + dev->priv_ = priv; +} + +void dev_set_parent_priv(struct udevice *dev, void *parent_priv) +{ + dev->parent_priv_ = parent_priv; +} + +void dev_set_uclass_priv(struct udevice *dev, void *uclass_priv) +{ + dev->uclass_priv_ = uclass_priv; +} + +void dev_set_plat(struct udevice *dev, void *plat) +{ + dev->plat_ = plat; +} + +void dev_set_parent_plat(struct udevice *dev, void *parent_plat) +{ + dev->parent_plat_ = parent_plat; +} + +void dev_set_uclass_plat(struct udevice *dev, void *uclass_plat) +{ + dev->uclass_plat_ = uclass_plat; +} + +#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) +bool device_is_compatible(const struct udevice *dev, const char *compat) +{ + return ofnode_device_is_compatible(dev_ofnode(dev), compat); +} + +bool of_machine_is_compatible(const char *compat) +{ + const void *fdt = gd->fdt_blob; + + return !fdt_node_check_compatible(fdt, 0, compat); +} + +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; + + list_for_each_entry(uc, gd->uclass_root, sibling_node) { + 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; + + return lists_bind_fdt(parent, node, NULL, false); +} +#endif + +#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 */ diff --git a/roms/u-boot/drivers/core/devres.c b/roms/u-boot/drivers/core/devres.c new file mode 100644 index 000000000..313ddc708 --- /dev/null +++ b/roms/u-boot/drivers/core/devres.c @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * 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> + */ + +#define LOG_CATEGORY LOGC_DEVRES + +#include <common.h> +#include <log.h> +#include <malloc.h> +#include <linux/compat.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <dm/device.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <dm/util.h> + +/** enum devres_phase - Shows where resource was allocated + * + * DEVRES_PHASE_BIND: In the bind() method + * DEVRES_PHASE_OFDATA: In the of_to_plat() method + * DEVRES_PHASE_PROBE: In the probe() method + */ +enum devres_phase { + DEVRES_PHASE_BIND, + DEVRES_PHASE_OFDATA, + DEVRES_PHASE_PROBE, +}; + +/** + * 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: Show where this resource was allocated + * @name: Name of release function + * @size: Size of resource data + * @data: Resource data + */ +struct devres { + struct list_head entry; + dr_release_t release; + enum devres_phase phase; +#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) +{ + log_debug("%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); + + assert_noisy(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"); + assert_noisy(list_empty(&dr->entry)); + if (dev_get_flags(dev) & DM_FLAG_PLATDATA_VALID) + dr->phase = DEVRES_PHASE_PROBE; + else if (dev_get_flags(dev) & DM_FLAG_BOUND) + dr->phase = DEVRES_PHASE_OFDATA; + else + dr->phase = DEVRES_PHASE_BIND; + 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_and_ofdata_only) +{ + struct devres *dr, *tmp; + + list_for_each_entry_safe_reverse(dr, tmp, head, entry) { + if (probe_and_ofdata_only && dr->phase == DEVRES_PHASE_BIND) + 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 char *const devres_phase_name[] = {"BIND", "OFDATA", "PROBE"}; + +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, + devres_phase_name[dr->phase]); + + 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); +} + +void devres_get_stats(const struct udevice *dev, struct devres_stats *stats) +{ + struct devres *dr; + + stats->allocs = 0; + stats->total_size = 0; + list_for_each_entry(dr, &dev->devres_head, entry) { + stats->allocs++; + stats->total_size += dr->size; + } +} + +#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); + assert_noisy(!rc); +} diff --git a/roms/u-boot/drivers/core/dump.c b/roms/u-boot/drivers/core/dump.c new file mode 100644 index 000000000..f8afea30a --- /dev/null +++ b/roms/u-boot/drivers/core/dump.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2015 Google, Inc + */ + +#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <dm/root.h> +#include <dm/util.h> +#include <dm/uclass-internal.h> + +static void show_devices(struct udevice *dev, int depth, int last_flag) +{ + int i, is_last; + struct udevice *child; + u32 flags = dev_get_flags(dev); + + /* print the first 20 characters to not break the tree-format. */ + printf(IS_ENABLED(CONFIG_SPL_BUILD) ? " %s %d [ %c ] %s " : + " %-10.10s %3d [ %c ] %-20.20s ", dev->uclass->uc_drv->name, + dev_get_uclass_index(dev, NULL), + flags & DM_FLAG_ACTIVATED ? '+' : ' ', dev->driver->name); + + 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 Index Probed Driver 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, int index) +{ + printf("%-3i %c %s @ %08lx", index, + dev_get_flags(dev) & DM_FLAG_ACTIVATED ? '*' : ' ', + dev->name, (ulong)map_to_sysmem(dev)); + if (dev->seq_ != -1) + printf(", seq %d", dev_seq(dev)); + puts("\n"); +} + +void dm_dump_uclass(void) +{ + struct uclass *uc; + int ret; + int id; + + for (id = 0; id < UCLASS_COUNT; id++) { + struct udevice *dev; + int i = 0; + + 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; + uclass_foreach_dev(dev, uc) { + dm_display_line(dev, i); + i++; + } + puts("\n"); + } +} + +void dm_dump_driver_compat(void) +{ + struct driver *d = ll_entry_start(struct driver, driver); + const int n_ents = ll_entry_count(struct driver, driver); + struct driver *entry; + const struct udevice_id *match; + + puts("Driver Compatible\n"); + puts("--------------------------------\n"); + for (entry = d; entry < d + n_ents; entry++) { + match = entry->of_match; + + printf("%-20.20s", entry->name); + if (match) { + printf(" %s", match->compatible); + match++; + } + printf("\n"); + + for (; match && match->compatible; match++) + printf("%-20.20s %s\n", "", match->compatible); + } +} + +void dm_dump_drivers(void) +{ + struct driver *d = ll_entry_start(struct driver, driver); + const int n_ents = ll_entry_count(struct driver, driver); + struct driver *entry; + struct udevice *udev; + struct uclass *uc; + int i; + + puts("Driver uid uclass Devices\n"); + puts("----------------------------------------------------------\n"); + + for (entry = d; entry < d + n_ents; entry++) { + uclass_get(entry->id, &uc); + + printf("%-25.25s %-3.3d %-20.20s ", entry->name, entry->id, + uc ? uc->uc_drv->name : "<no uclass>"); + + if (!uc) { + puts("\n"); + continue; + } + + i = 0; + uclass_foreach_dev(udev, uc) { + if (udev->driver != entry) + continue; + if (i) + printf("%-51.51s", ""); + + printf("%-25.25s\n", udev->name); + i++; + } + if (!i) + puts("<none>\n"); + } +} + +void dm_dump_static_driver_info(void) +{ + struct driver_info *drv = ll_entry_start(struct driver_info, + driver_info); + const int n_ents = ll_entry_count(struct driver_info, driver_info); + struct driver_info *entry; + + puts("Driver Address\n"); + puts("---------------------------------\n"); + for (entry = drv; entry != drv + n_ents; entry++) { + printf("%-25.25s @%08lx\n", entry->name, + (ulong)map_to_sysmem(entry->plat)); + } +} diff --git a/roms/u-boot/drivers/core/fdtaddr.c b/roms/u-boot/drivers/core/fdtaddr.c new file mode 100644 index 000000000..b9874c743 --- /dev/null +++ b/roms/u-boot/drivers/core/fdtaddr.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Device addresses + * + * Copyright (c) 2017 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + */ + +#include <common.h> +#include <dm.h> +#include <fdt_support.h> +#include <log.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <dm/device-internal.h> + +DECLARE_GLOBAL_DATA_PTR; + +fdt_addr_t devfdt_get_addr_index(const struct udevice *dev, int index) +{ +#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) + 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_of_offset(dev->parent)); + if (na < 1) { + debug("bad #address-cells\n"); + return FDT_ADDR_T_NONE; + } + + ns = fdt_size_cells(gd->fdt_blob, dev_of_offset(dev->parent)); + if (ns < 0) { + debug("bad #size-cells\n"); + return FDT_ADDR_T_NONE; + } + + reg = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), "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); + + if (ns) { + /* + * Use the full-fledged translate function for complex + * bus setups. + */ + addr = fdt_translate_address((void *)gd->fdt_blob, + dev_of_offset(dev), reg); + } else { + /* Non translatable if #size-cells == 0 */ + addr = fdt_read_number(reg, na); + } + } else { + /* + * Use the "simple" translate function for less complex + * bus setups. + */ + addr = fdtdec_get_addr_size_auto_parent(gd->fdt_blob, + dev_of_offset(dev->parent), dev_of_offset(dev), + "reg", index, NULL, false); + 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); + } + } + +#if defined(CONFIG_TRANSLATION_OFFSET) + /* + * Some platforms need a special address translation. Those + * platforms (e.g. mvebu in SPL) can configure a translation + * offset by setting this value in the GD and enaling this + * feature via CONFIG_TRANSLATION_OFFSET. This value will + * get added to all addresses returned by devfdt_get_addr(). + */ + addr += gd->translation_offset; +#endif + + return addr; +#else + return FDT_ADDR_T_NONE; +#endif +} + +fdt_addr_t devfdt_get_addr_size_index(const struct udevice *dev, int index, + fdt_size_t *size) +{ +#if CONFIG_IS_ENABLED(OF_CONTROL) + /* + * Only get the size in this first call. We'll get the addr in the + * next call to the exisiting dev_get_xxx function which handles + * all config options. + */ + fdtdec_get_addr_size_auto_noparent(gd->fdt_blob, dev_of_offset(dev), + "reg", index, size, false); + + /* + * Get the base address via the existing function which handles + * all Kconfig cases + */ + return devfdt_get_addr_index(dev, index); +#else + return FDT_ADDR_T_NONE; +#endif +} + +fdt_addr_t devfdt_get_addr_name(const struct udevice *dev, const char *name) +{ +#if CONFIG_IS_ENABLED(OF_CONTROL) + int index; + + index = fdt_stringlist_search(gd->fdt_blob, dev_of_offset(dev), + "reg-names", name); + if (index < 0) + return index; + + return devfdt_get_addr_index(dev, index); +#else + return FDT_ADDR_T_NONE; +#endif +} + +fdt_addr_t devfdt_get_addr_size_name(const struct udevice *dev, + const char *name, fdt_size_t *size) +{ +#if CONFIG_IS_ENABLED(OF_CONTROL) + int index; + + index = fdt_stringlist_search(gd->fdt_blob, dev_of_offset(dev), + "reg-names", name); + if (index < 0) + return index; + + return devfdt_get_addr_size_index(dev, index, size); +#else + return FDT_ADDR_T_NONE; +#endif +} + +fdt_addr_t devfdt_get_addr(const struct udevice *dev) +{ + return devfdt_get_addr_index(dev, 0); +} + +void *devfdt_get_addr_ptr(const struct udevice *dev) +{ + fdt_addr_t addr = devfdt_get_addr_index(dev, 0); + + return (addr == FDT_ADDR_T_NONE) ? NULL : (void *)(uintptr_t)addr; +} + +void *devfdt_remap_addr_index(const struct udevice *dev, int index) +{ + fdt_addr_t addr = devfdt_get_addr_index(dev, index); + + if (addr == FDT_ADDR_T_NONE) + return NULL; + + return map_physmem(addr, 0, MAP_NOCACHE); +} + +void *devfdt_remap_addr_name(const struct udevice *dev, const char *name) +{ + fdt_addr_t addr = devfdt_get_addr_name(dev, name); + + if (addr == FDT_ADDR_T_NONE) + return NULL; + + return map_physmem(addr, 0, MAP_NOCACHE); +} + +void *devfdt_remap_addr(const struct udevice *dev) +{ + return devfdt_remap_addr_index(dev, 0); +} + +void *devfdt_map_physmem(const struct udevice *dev, unsigned long size) +{ + fdt_addr_t addr = devfdt_get_addr(dev); + + if (addr == FDT_ADDR_T_NONE) + return NULL; + + return map_physmem(addr, size, MAP_NOCACHE); +} + +fdt_addr_t devfdt_get_addr_pci(const struct udevice *dev) +{ + ulong addr; + + addr = devfdt_get_addr(dev); + if (CONFIG_IS_ENABLED(PCI) && IS_ENABLED(CONFIG_DM_PCI) && + addr == FDT_ADDR_T_NONE) { + struct fdt_pci_addr pci_addr; + u32 bar; + int ret; + + ret = ofnode_read_pci_addr(dev_ofnode(dev), FDT_PCI_SPACE_MEM32, + "reg", &pci_addr); + if (ret) { + /* try if there is any i/o-mapped register */ + ret = ofnode_read_pci_addr(dev_ofnode(dev), + FDT_PCI_SPACE_IO, "reg", + &pci_addr); + if (ret) + return FDT_ADDR_T_NONE; + } + ret = fdtdec_get_pci_bar32(dev, &pci_addr, &bar); + if (ret) + return FDT_ADDR_T_NONE; + addr = bar; + } + + return addr; +} diff --git a/roms/u-boot/drivers/core/lists.c b/roms/u-boot/drivers/core/lists.c new file mode 100644 index 000000000..e214306b9 --- /dev/null +++ b/roms/u-boot/drivers/core/lists.c @@ -0,0 +1,268 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Marek Vasut <marex@denx.de> + */ + +#define LOG_CATEGORY LOGC_DM + +#include <common.h> +#include <errno.h> +#include <log.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_driver); + const int n_ents = ll_entry_count(struct uclass_driver, uclass_driver); + struct uclass_driver *entry; + + for (entry = uclass; entry != uclass + n_ents; entry++) { + if (entry->id == id) + return entry; + } + + return NULL; +} + +static int bind_drivers_pass(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); + bool missing_parent = false; + int result = 0; + uint idx; + + /* + * Do one iteration through the driver_info records. For of-platdata, + * bind only devices whose parent is already bound. If we find any + * device we can't bind, set missing_parent to true, which will cause + * this function to be called again. + */ + for (idx = 0; idx < n_ents; idx++) { + struct udevice *par = parent; + const struct driver_info *entry = info + idx; + struct driver_rt *drt = gd_dm_driver_rt() + idx; + struct udevice *dev; + int ret; + + if (CONFIG_IS_ENABLED(OF_PLATDATA)) { + int parent_idx = driver_info_parent_id(entry); + + if (drt->dev) + continue; + + if (CONFIG_IS_ENABLED(OF_PLATDATA_PARENT) && + parent_idx != -1) { + struct driver_rt *parent_drt; + + parent_drt = gd_dm_driver_rt() + parent_idx; + if (!parent_drt->dev) { + missing_parent = true; + continue; + } + + par = parent_drt->dev; + } + } + ret = device_bind_by_name(par, pre_reloc_only, entry, &dev); + if (!ret) { + if (CONFIG_IS_ENABLED(OF_PLATDATA)) + drt->dev = dev; + } else if (ret != -EPERM) { + dm_warn("No match for driver '%s'\n", entry->name); + if (!result || ret != -ENOENT) + result = ret; + } + } + + return result ? result : missing_parent ? -EAGAIN : 0; +} + +int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only) +{ + int result = 0; + int pass; + + /* + * 10 passes is 10 levels deep in the devicetree, which is plenty. If + * OF_PLATDATA_PARENT is not enabled, then bind_drivers_pass() will + * always succeed on the first pass. + */ + for (pass = 0; pass < 10; pass++) { + int ret; + + ret = bind_drivers_pass(parent, pre_reloc_only); + if (!ret) + break; + if (ret != -EAGAIN && !result) + 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, + ofnode_null(), devp); +} + +int device_bind_driver_to_node(struct udevice *parent, const char *drv_name, + const char *dev_name, ofnode 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_with_driver_data(parent, drv, dev_name, 0 /* data */, + node, devp); + + return ret; +} + +#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) +/** + * driver_check_compatible() - Check if a driver matches a compatible string + * + * @param of_match: List of compatible strings to match + * @param of_idp: Returns the match that was found + * @param compat: The compatible string to search for + * @return 0 if there is a match, -ENOENT if no match + */ +static int driver_check_compatible(const struct udevice_id *of_match, + const struct udevice_id **of_idp, + const char *compat) +{ + if (!of_match) + return -ENOENT; + + while (of_match->compatible) { + if (!strcmp(of_match->compatible, compat)) { + *of_idp = of_match; + return 0; + } + of_match++; + } + + return -ENOENT; +} + +int lists_bind_fdt(struct udevice *parent, ofnode node, struct udevice **devp, + bool pre_reloc_only) +{ + 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, *compat_list, *compat; + int compat_length, i; + int result = 0; + int ret = 0; + + if (devp) + *devp = NULL; + name = ofnode_get_name(node); + log_debug("bind node %s\n", name); + + compat_list = ofnode_get_property(node, "compatible", &compat_length); + if (!compat_list) { + if (compat_length == -FDT_ERR_NOTFOUND) { + log_debug("Device '%s' has no compatible string\n", + name); + return 0; + } + + dm_warn("Device tree error at node '%s'\n", name); + return compat_length; + } + + /* + * Walk through the compatible string list, attempting to match each + * compatible string in order such that we match in order of priority + * from the first string to the last. + */ + for (i = 0; i < compat_length; i += strlen(compat) + 1) { + compat = compat_list + i; + log_debug(" - attempt to match compatible string '%s'\n", + compat); + + for (entry = driver; entry != driver + n_ents; entry++) { + ret = driver_check_compatible(entry->of_match, &id, + compat); + if (!ret) + break; + } + if (entry == driver + n_ents) + continue; + + if (pre_reloc_only) { + if (!ofnode_pre_reloc(node) && + !(entry->flags & DM_FLAG_PRE_RELOC)) { + log_debug("Skipping device pre-relocation\n"); + return 0; + } + } + + log_debug(" - found match at '%s': '%s' matches '%s'\n", + entry->name, entry->of_match->compatible, + id->compatible); + ret = device_bind_with_driver_data(parent, entry, name, + id->data, node, &dev); + if (ret == -ENODEV) { + log_debug("Driver '%s' refuses to bind\n", entry->name); + continue; + } + if (ret) { + dm_warn("Error binding driver '%s': %d\n", entry->name, + ret); + return log_msg_ret("bind", ret); + } else { + found = true; + if (devp) + *devp = dev; + } + break; + } + + if (!found && !result && ret != -ENODEV) + log_debug("No match for node '%s'\n", name); + + return result; +} +#endif diff --git a/roms/u-boot/drivers/core/of_access.c b/roms/u-boot/drivers/core/of_access.c new file mode 100644 index 000000000..9960e6b31 --- /dev/null +++ b/roms/u-boot/drivers/core/of_access.c @@ -0,0 +1,882 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Originally from Linux v4.9 + * Paul Mackerras August 1996. + * Copyright (C) 1996-2005 Paul Mackerras. + * + * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner. + * {engebret|bergner}@us.ibm.com + * + * Adapted for sparc and sparc64 by David S. Miller davem@davemloft.net + * + * Reconsolidated from arch/x/kernel/prom.c by Stephen Rothwell and + * Grant Likely. + * + * Modified for U-Boot + * Copyright (c) 2017 Google, Inc + * + * This file follows drivers/of/base.c with functions in the same order as the + * Linux version. + */ + +#include <common.h> +#include <log.h> +#include <malloc.h> +#include <asm/global_data.h> +#include <linux/bug.h> +#include <linux/libfdt.h> +#include <dm/of_access.h> +#include <linux/ctype.h> +#include <linux/err.h> +#include <linux/ioport.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* list of struct alias_prop aliases */ +LIST_HEAD(aliases_lookup); + +/* "/aliaes" node */ +static struct device_node *of_aliases; + +/* "/chosen" node */ +static struct device_node *of_chosen; + +/* node pointed to by the stdout-path alias */ +static struct device_node *of_stdout; + +/* pointer to options given after the alias (separated by :) or NULL if none */ +static const char *of_stdout_options; + +/** + * struct alias_prop - Alias property in 'aliases' node + * + * The structure represents one alias property of 'aliases' node as + * an entry in aliases_lookup list. + * + * @link: List node to link the structure in aliases_lookup list + * @alias: Alias property name + * @np: Pointer to device_node that the alias stands for + * @id: Index value from end of alias name + * @stem: Alias string without the index + */ +struct alias_prop { + struct list_head link; + const char *alias; + struct device_node *np; + int id; + char stem[0]; +}; + +int of_n_addr_cells(const struct device_node *np) +{ + const __be32 *ip; + + do { + if (np->parent) + np = np->parent; + ip = of_get_property(np, "#address-cells", NULL); + if (ip) + return be32_to_cpup(ip); + } while (np->parent); + + /* No #address-cells property for the root node */ + return OF_ROOT_NODE_ADDR_CELLS_DEFAULT; +} + +int of_n_size_cells(const struct device_node *np) +{ + const __be32 *ip; + + do { + if (np->parent) + np = np->parent; + ip = of_get_property(np, "#size-cells", NULL); + if (ip) + return be32_to_cpup(ip); + } while (np->parent); + + /* No #size-cells property for the root node */ + return OF_ROOT_NODE_SIZE_CELLS_DEFAULT; +} + +int of_simple_addr_cells(const struct device_node *np) +{ + const __be32 *ip; + + ip = of_get_property(np, "#address-cells", NULL); + if (ip) + return be32_to_cpup(ip); + + /* Return a default of 2 to match fdt_address_cells()*/ + return 2; +} + +int of_simple_size_cells(const struct device_node *np) +{ + const __be32 *ip; + + ip = of_get_property(np, "#size-cells", NULL); + if (ip) + return be32_to_cpup(ip); + + /* Return a default of 2 to match fdt_size_cells()*/ + return 2; +} + +struct property *of_find_property(const struct device_node *np, + const char *name, int *lenp) +{ + struct property *pp; + + if (!np) + return NULL; + + for (pp = np->properties; pp; pp = pp->next) { + if (strcmp(pp->name, name) == 0) { + if (lenp) + *lenp = pp->length; + break; + } + } + if (!pp && lenp) + *lenp = -FDT_ERR_NOTFOUND; + + return pp; +} + +struct device_node *of_find_all_nodes(struct device_node *prev) +{ + struct device_node *np; + + if (!prev) { + np = gd->of_root; + } else if (prev->child) { + np = prev->child; + } else { + /* + * Walk back up looking for a sibling, or the end of the + * structure + */ + np = prev; + while (np->parent && !np->sibling) + np = np->parent; + np = np->sibling; /* Might be null at the end of the tree */ + } + + return np; +} + +const void *of_get_property(const struct device_node *np, const char *name, + int *lenp) +{ + struct property *pp = of_find_property(np, name, lenp); + + return pp ? pp->value : NULL; +} + +const struct property *of_get_first_property(const struct device_node *np) +{ + if (!np) + return NULL; + + return np->properties; +} + +const struct property *of_get_next_property(const struct device_node *np, + const struct property *property) +{ + if (!np) + return NULL; + + return property->next; +} + +const void *of_get_property_by_prop(const struct device_node *np, + const struct property *property, + const char **name, + int *lenp) +{ + if (!np || !property) + return NULL; + if (name) + *name = property->name; + if (lenp) + *lenp = property->length; + + return property->value; +} + +static const char *of_prop_next_string(struct property *prop, const char *cur) +{ + const void *curv = cur; + + if (!prop) + return NULL; + + if (!cur) + return prop->value; + + curv += strlen(cur) + 1; + if (curv >= prop->value + prop->length) + return NULL; + + return curv; +} + +int of_device_is_compatible(const struct device_node *device, + const char *compat, const char *type, + const char *name) +{ + struct property *prop; + const char *cp; + int index = 0, score = 0; + + /* Compatible match has highest priority */ + if (compat && compat[0]) { + prop = of_find_property(device, "compatible", NULL); + for (cp = of_prop_next_string(prop, NULL); cp; + cp = of_prop_next_string(prop, cp), index++) { + if (of_compat_cmp(cp, compat, strlen(compat)) == 0) { + score = INT_MAX/2 - (index << 2); + break; + } + } + if (!score) + return 0; + } + + /* Matching type is better than matching name */ + if (type && type[0]) { + if (!device->type || of_node_cmp(type, device->type)) + return 0; + score += 2; + } + + /* Matching name is a bit better than not */ + if (name && name[0]) { + if (!device->name || of_node_cmp(name, device->name)) + return 0; + score++; + } + + return score; +} + +bool of_device_is_available(const struct device_node *device) +{ + const char *status; + int statlen; + + if (!device) + return false; + + status = of_get_property(device, "status", &statlen); + if (status == NULL) + return true; + + if (statlen > 0) { + if (!strcmp(status, "okay")) + return true; + } + + return false; +} + +struct device_node *of_get_parent(const struct device_node *node) +{ + const struct device_node *np; + + if (!node) + return NULL; + + np = of_node_get(node->parent); + + return (struct device_node *)np; +} + +static struct device_node *__of_get_next_child(const struct device_node *node, + struct device_node *prev) +{ + struct device_node *next; + + if (!node) + return NULL; + + next = prev ? prev->sibling : node->child; + /* + * coverity[dead_error_line : FALSE] + * Dead code here since our current implementation of of_node_get() + * always returns NULL (Coverity CID 163245). But we leave it as is + * since we may want to implement get/put later. + */ + for (; next; next = next->sibling) + if (of_node_get(next)) + break; + of_node_put(prev); + return next; +} + +#define __for_each_child_of_node(parent, child) \ + for (child = __of_get_next_child(parent, NULL); child != NULL; \ + child = __of_get_next_child(parent, child)) + +static struct device_node *__of_find_node_by_path(struct device_node *parent, + const char *path) +{ + struct device_node *child; + int len; + + len = strcspn(path, "/:"); + if (!len) + return NULL; + + __for_each_child_of_node(parent, child) { + const char *name = strrchr(child->full_name, '/'); + + name++; + if (strncmp(path, name, len) == 0 && (strlen(name) == len)) + return child; + } + return NULL; +} + +#define for_each_property_of_node(dn, pp) \ + for (pp = dn->properties; pp != NULL; pp = pp->next) + +struct device_node *of_find_node_opts_by_path(const char *path, + const char **opts) +{ + struct device_node *np = NULL; + struct property *pp; + const char *separator = strchr(path, ':'); + + if (opts) + *opts = separator ? separator + 1 : NULL; + + if (strcmp(path, "/") == 0) + return of_node_get(gd->of_root); + + /* The path could begin with an alias */ + if (*path != '/') { + int len; + const char *p = separator; + + if (!p) + p = strchrnul(path, '/'); + len = p - path; + + /* of_aliases must not be NULL */ + if (!of_aliases) + return NULL; + + for_each_property_of_node(of_aliases, pp) { + if (strlen(pp->name) == len && !strncmp(pp->name, path, + len)) { + np = of_find_node_by_path(pp->value); + break; + } + } + if (!np) + return NULL; + path = p; + } + + /* Step down the tree matching path components */ + if (!np) + np = of_node_get(gd->of_root); + while (np && *path == '/') { + struct device_node *tmp = np; + + path++; /* Increment past '/' delimiter */ + np = __of_find_node_by_path(np, path); + of_node_put(tmp); + path = strchrnul(path, '/'); + if (separator && separator < path) + break; + } + + return np; +} + +struct device_node *of_find_compatible_node(struct device_node *from, + const char *type, const char *compatible) +{ + struct device_node *np; + + for_each_of_allnodes_from(from, np) + if (of_device_is_compatible(np, compatible, type, NULL) && + of_node_get(np)) + break; + of_node_put(from); + + return np; +} + +static int of_device_has_prop_value(const struct device_node *device, + const char *propname, const void *propval, + int proplen) +{ + struct property *prop = of_find_property(device, propname, NULL); + + if (!prop || !prop->value || prop->length != proplen) + return 0; + return !memcmp(prop->value, propval, proplen); +} + +struct device_node *of_find_node_by_prop_value(struct device_node *from, + const char *propname, + const void *propval, int proplen) +{ + struct device_node *np; + + for_each_of_allnodes_from(from, np) { + if (of_device_has_prop_value(np, propname, propval, proplen) && + of_node_get(np)) + break; + } + of_node_put(from); + + return np; +} + +struct device_node *of_find_node_by_phandle(phandle handle) +{ + struct device_node *np; + + if (!handle) + return NULL; + + for_each_of_allnodes(np) + if (np->phandle == handle) + break; + (void)of_node_get(np); + + return np; +} + +/** + * of_find_property_value_of_size() - find property of given size + * + * Search for a property in a device node and validate the requested size. + * + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @len: requested length of property value + * + * @return the property value on success, -EINVAL if the property does not + * exist, -ENODATA if property does not have a value, and -EOVERFLOW if the + * property data isn't large enough. + */ +static void *of_find_property_value_of_size(const struct device_node *np, + const char *propname, u32 len) +{ + struct property *prop = of_find_property(np, propname, NULL); + + if (!prop) + return ERR_PTR(-EINVAL); + if (!prop->value) + return ERR_PTR(-ENODATA); + if (len > prop->length) + return ERR_PTR(-EOVERFLOW); + + return prop->value; +} + +int of_read_u32(const struct device_node *np, const char *propname, u32 *outp) +{ + return of_read_u32_index(np, propname, 0, outp); +} + +int of_read_u32_array(const struct device_node *np, const char *propname, + u32 *out_values, size_t sz) +{ + const __be32 *val; + + debug("%s: %s: ", __func__, propname); + val = of_find_property_value_of_size(np, propname, + sz * sizeof(*out_values)); + + if (IS_ERR(val)) + return PTR_ERR(val); + + debug("size %zd\n", sz); + while (sz--) + *out_values++ = be32_to_cpup(val++); + + return 0; +} + +int of_read_u32_index(const struct device_node *np, const char *propname, + int index, u32 *outp) +{ + const __be32 *val; + + debug("%s: %s: ", __func__, propname); + if (!np) + return -EINVAL; + + val = of_find_property_value_of_size(np, propname, + sizeof(*outp) * (index + 1)); + if (IS_ERR(val)) { + debug("(not found)\n"); + return PTR_ERR(val); + } + + *outp = be32_to_cpup(val + index); + debug("%#x (%d)\n", *outp, *outp); + + return 0; +} + +int of_read_u64(const struct device_node *np, const char *propname, u64 *outp) +{ + const __be64 *val; + + debug("%s: %s: ", __func__, propname); + if (!np) + return -EINVAL; + val = of_find_property_value_of_size(np, propname, sizeof(*outp)); + if (IS_ERR(val)) { + debug("(not found)\n"); + return PTR_ERR(val); + } + + *outp = be64_to_cpup(val); + debug("%#llx (%lld)\n", (unsigned long long)*outp, + (unsigned long long)*outp); + + return 0; +} + +int of_property_match_string(const struct device_node *np, const char *propname, + const char *string) +{ + const struct property *prop = of_find_property(np, propname, NULL); + size_t l; + int i; + const char *p, *end; + + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + + p = prop->value; + end = p + prop->length; + + for (i = 0; p < end; i++, p += l) { + l = strnlen(p, end - p) + 1; + if (p + l > end) + return -EILSEQ; + debug("comparing %s with %s\n", string, p); + if (strcmp(string, p) == 0) + return i; /* Found it; return index */ + } + return -ENODATA; +} + +/** + * of_property_read_string_helper() - Utility helper for parsing string properties + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @out_strs: output array of string pointers. + * @sz: number of array elements to read. + * @skip: Number of strings to skip over at beginning of list. + * + * Don't call this function directly. It is a utility helper for the + * of_property_read_string*() family of functions. + */ +int of_property_read_string_helper(const struct device_node *np, + const char *propname, const char **out_strs, + size_t sz, int skip) +{ + const struct property *prop = of_find_property(np, propname, NULL); + int l = 0, i = 0; + const char *p, *end; + + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + p = prop->value; + end = p + prop->length; + + for (i = 0; p < end && (!out_strs || i < skip + sz); i++, p += l) { + l = strnlen(p, end - p) + 1; + if (p + l > end) + return -EILSEQ; + if (out_strs && i >= skip) + *out_strs++ = p; + } + i -= skip; + return i <= 0 ? -ENODATA : i; +} + +static int __of_parse_phandle_with_args(const struct device_node *np, + const char *list_name, + const char *cells_name, + int cell_count, int index, + struct of_phandle_args *out_args) +{ + const __be32 *list, *list_end; + int rc = 0, cur_index = 0; + uint32_t count; + struct device_node *node = NULL; + phandle phandle; + int size; + + /* Retrieve the phandle list property */ + list = of_get_property(np, list_name, &size); + if (!list) + return -ENOENT; + list_end = list + size / sizeof(*list); + + /* Loop over the phandles until all the requested entry is found */ + while (list < list_end) { + rc = -EINVAL; + count = 0; + + /* + * If phandle is 0, then it is an empty entry with no + * arguments. Skip forward to the next entry. + */ + phandle = be32_to_cpup(list++); + if (phandle) { + /* + * Find the provider node and parse the #*-cells + * property to determine the argument length. + * + * This is not needed if the cell count is hard-coded + * (i.e. cells_name not set, but cell_count is set), + * except when we're going to return the found node + * below. + */ + if (cells_name || cur_index == index) { + node = of_find_node_by_phandle(phandle); + if (!node) { + debug("%s: could not find phandle\n", + np->full_name); + goto err; + } + } + + if (cells_name) { + if (of_read_u32(node, cells_name, &count)) { + debug("%s: could not get %s for %s\n", + np->full_name, cells_name, + node->full_name); + goto err; + } + } else { + count = cell_count; + } + + /* + * Make sure that the arguments actually fit in the + * remaining property data length + */ + if (list + count > list_end) { + debug("%s: arguments longer than property\n", + np->full_name); + goto err; + } + } + + /* + * All of the error cases above bail out of the loop, so at + * this point, the parsing is successful. If the requested + * index matches, then fill the out_args structure and return, + * or return -ENOENT for an empty entry. + */ + rc = -ENOENT; + if (cur_index == index) { + if (!phandle) + goto err; + + if (out_args) { + int i; + if (WARN_ON(count > OF_MAX_PHANDLE_ARGS)) + count = OF_MAX_PHANDLE_ARGS; + out_args->np = node; + out_args->args_count = count; + for (i = 0; i < count; i++) + out_args->args[i] = + be32_to_cpup(list++); + } else { + of_node_put(node); + } + + /* Found it! return success */ + return 0; + } + + of_node_put(node); + node = NULL; + list += count; + cur_index++; + } + + /* + * Unlock node before returning result; will be one of: + * -ENOENT : index is for empty phandle + * -EINVAL : parsing error on data + * [1..n] : Number of phandle (count mode; when index = -1) + */ + rc = index < 0 ? cur_index : -ENOENT; + err: + if (node) + of_node_put(node); + return rc; +} + +struct device_node *of_parse_phandle(const struct device_node *np, + const char *phandle_name, int index) +{ + struct of_phandle_args args; + + if (index < 0) + return NULL; + + if (__of_parse_phandle_with_args(np, phandle_name, NULL, 0, index, + &args)) + return NULL; + + return args.np; +} + +int of_parse_phandle_with_args(const struct device_node *np, + const char *list_name, const char *cells_name, + int cell_count, int index, + struct of_phandle_args *out_args) +{ + if (index < 0) + return -EINVAL; + + return __of_parse_phandle_with_args(np, list_name, cells_name, + cell_count, index, out_args); +} + +int of_count_phandle_with_args(const struct device_node *np, + const char *list_name, const char *cells_name, + int cell_count) +{ + return __of_parse_phandle_with_args(np, list_name, cells_name, + cell_count, -1, NULL); +} + +static void of_alias_add(struct alias_prop *ap, struct device_node *np, + int id, const char *stem, int stem_len) +{ + ap->np = np; + ap->id = id; + strncpy(ap->stem, stem, stem_len); + ap->stem[stem_len] = 0; + list_add_tail(&ap->link, &aliases_lookup); + debug("adding DT alias:%s: stem=%s id=%i node=%s\n", + ap->alias, ap->stem, ap->id, of_node_full_name(np)); +} + +int of_alias_scan(void) +{ + struct property *pp; + + of_aliases = of_find_node_by_path("/aliases"); + of_chosen = of_find_node_by_path("/chosen"); + if (of_chosen == NULL) + of_chosen = of_find_node_by_path("/chosen@0"); + + if (of_chosen) { + const char *name; + + name = of_get_property(of_chosen, "stdout-path", NULL); + if (name) + of_stdout = of_find_node_opts_by_path(name, + &of_stdout_options); + } + + if (!of_aliases) + return 0; + + for_each_property_of_node(of_aliases, pp) { + const char *start = pp->name; + const char *end = start + strlen(start); + struct device_node *np; + struct alias_prop *ap; + ulong id; + int len; + + /* Skip those we do not want to proceed */ + if (!strcmp(pp->name, "name") || + !strcmp(pp->name, "phandle") || + !strcmp(pp->name, "linux,phandle")) + continue; + + np = of_find_node_by_path(pp->value); + if (!np) + continue; + + /* + * walk the alias backwards to extract the id and work out + * the 'stem' string + */ + while (isdigit(*(end-1)) && end > start) + end--; + len = end - start; + + if (strict_strtoul(end, 10, &id) < 0) + continue; + + /* Allocate an alias_prop with enough space for the stem */ + ap = malloc(sizeof(*ap) + len + 1); + if (!ap) + return -ENOMEM; + memset(ap, 0, sizeof(*ap) + len + 1); + ap->alias = start; + of_alias_add(ap, np, id, start, len); + } + + return 0; +} + +int of_alias_get_id(const struct device_node *np, const char *stem) +{ + struct alias_prop *app; + int id = -ENODEV; + + mutex_lock(&of_mutex); + list_for_each_entry(app, &aliases_lookup, link) { + if (strcmp(app->stem, stem) != 0) + continue; + + if (np == app->np) { + id = app->id; + break; + } + } + mutex_unlock(&of_mutex); + + return id; +} + +int of_alias_get_highest_id(const char *stem) +{ + struct alias_prop *app; + int id = -1; + + mutex_lock(&of_mutex); + list_for_each_entry(app, &aliases_lookup, link) { + if (strcmp(app->stem, stem) != 0) + continue; + + if (app->id > id) + id = app->id; + } + mutex_unlock(&of_mutex); + + return id; +} + +struct device_node *of_get_stdout(void) +{ + return of_stdout; +} diff --git a/roms/u-boot/drivers/core/of_addr.c b/roms/u-boot/drivers/core/of_addr.c new file mode 100644 index 000000000..3fbc0a7af --- /dev/null +++ b/roms/u-boot/drivers/core/of_addr.c @@ -0,0 +1,440 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Taken from Linux v4.9 drivers/of/address.c + * + * Modified for U-Boot + * Copyright (c) 2017 Google, Inc + */ + +#include <common.h> +#include <log.h> +#include <linux/bug.h> +#include <linux/libfdt.h> +#include <dm/of_access.h> +#include <dm/of_addr.h> +#include <linux/err.h> +#include <linux/ioport.h> + +/* Max address size we deal with */ +#define OF_MAX_ADDR_CELLS 4 +#define OF_CHECK_ADDR_COUNT(na) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS) +#define OF_CHECK_COUNTS(na, ns) (OF_CHECK_ADDR_COUNT(na) && (ns) > 0) + +static struct of_bus *of_match_bus(struct device_node *np); + +/* Debug utility */ +#ifdef DEBUG +static void of_dump_addr(const char *s, const __be32 *addr, int na) +{ + debug("%s", s); + while (na--) + pr_cont(" %08x", be32_to_cpu(*(addr++))); + pr_cont("\n"); +} +#else +static void of_dump_addr(const char *s, const __be32 *addr, int na) { } +#endif + +/* Callbacks for bus specific translators */ +struct of_bus { + const char *name; + const char *addresses; + int (*match)(struct device_node *parent); + void (*count_cells)(const struct device_node *child, int *addrc, + int *sizec); + u64 (*map)(__be32 *addr, const __be32 *range, int na, int ns, int pna); + int (*translate)(__be32 *addr, u64 offset, int na); + unsigned int (*get_flags)(const __be32 *addr); +}; + +static void of_bus_default_count_cells(const struct device_node *np, + int *addrc, int *sizec) +{ + if (addrc) + *addrc = of_n_addr_cells(np); + if (sizec) + *sizec = of_n_size_cells(np); +} + +static u64 of_bus_default_map(__be32 *addr, const __be32 *range, + int na, int ns, int pna) +{ + u64 cp, s, da; + + cp = of_read_number(range, na); + s = of_read_number(range + na + pna, ns); + da = of_read_number(addr, na); + + debug("default map, cp=%llx, s=%llx, da=%llx\n", + (unsigned long long)cp, (unsigned long long)s, + (unsigned long long)da); + + if (da < cp || da >= (cp + s)) + return OF_BAD_ADDR; + return da - cp; +} + +static int of_bus_default_translate(__be32 *addr, u64 offset, int na) +{ + u64 a = of_read_number(addr, na); + memset(addr, 0, na * 4); + a += offset; + if (na > 1) + addr[na - 2] = cpu_to_be32(a >> 32); + addr[na - 1] = cpu_to_be32(a & 0xffffffffu); + + return 0; +} + +static unsigned int of_bus_default_get_flags(const __be32 *addr) +{ + return IORESOURCE_MEM; +} + +/* + * Array of bus-specific translators + */ +static struct of_bus of_busses[] = { + /* Default */ + { + .name = "default", + .addresses = "reg", + .match = NULL, + .count_cells = of_bus_default_count_cells, + .map = of_bus_default_map, + .translate = of_bus_default_translate, + .get_flags = of_bus_default_get_flags, + }, +}; + +static struct of_bus *of_match_bus(struct device_node *np) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(of_busses); i++) + if (!of_busses[i].match || of_busses[i].match(np)) + return &of_busses[i]; + BUG(); + return NULL; +} + +const __be32 *of_get_address(const struct device_node *dev, int index, + u64 *size, unsigned int *flags) +{ + const __be32 *prop; + int psize; + struct device_node *parent; + struct of_bus *bus; + int onesize, i, na, ns; + + /* Get parent & match bus type */ + parent = of_get_parent(dev); + if (parent == NULL) + return NULL; + bus = of_match_bus(parent); + bus->count_cells(dev, &na, &ns); + of_node_put(parent); + if (!OF_CHECK_ADDR_COUNT(na)) + return NULL; + + /* Get "reg" or "assigned-addresses" property */ + prop = of_get_property(dev, "reg", &psize); + if (prop == NULL) + return NULL; + psize /= 4; + + onesize = na + ns; + for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++) + if (i == index) { + if (size) + *size = of_read_number(prop + na, ns); + if (flags) + *flags = bus->get_flags(prop); + return prop; + } + return NULL; +} +EXPORT_SYMBOL(of_get_address); + +static int of_empty_ranges_quirk(const struct device_node *np) +{ + return false; +} + +static int of_translate_one(const struct device_node *parent, + struct of_bus *bus, struct of_bus *pbus, + __be32 *addr, int na, int ns, int pna, + const char *rprop) +{ + const __be32 *ranges; + int rlen; + int rone; + u64 offset = OF_BAD_ADDR; + + /* + * Normally, an absence of a "ranges" property means we are + * crossing a non-translatable boundary, and thus the addresses + * below the current cannot be converted to CPU physical ones. + * Unfortunately, while this is very clear in the spec, it's not + * what Apple understood, and they do have things like /uni-n or + * /ht nodes with no "ranges" property and a lot of perfectly + * useable mapped devices below them. Thus we treat the absence of + * "ranges" as equivalent to an empty "ranges" property which means + * a 1:1 translation at that level. It's up to the caller not to try + * to translate addresses that aren't supposed to be translated in + * the first place. --BenH. + * + * As far as we know, this damage only exists on Apple machines, so + * This code is only enabled on powerpc. --gcl + * + * This quirk also applies for 'dma-ranges' which frequently exist in + * child nodes without 'dma-ranges' in the parent nodes. --RobH + */ + ranges = of_get_property(parent, rprop, &rlen); + if (ranges == NULL && !of_empty_ranges_quirk(parent) && + strcmp(rprop, "dma-ranges")) { + debug("no ranges; cannot translate\n"); + return 1; + } + if (ranges == NULL || rlen == 0) { + offset = of_read_number(addr, na); + memset(addr, 0, pna * 4); + debug("empty ranges; 1:1 translation\n"); + goto finish; + } + + debug("walking ranges...\n"); + + /* Now walk through the ranges */ + rlen /= 4; + rone = na + pna + ns; + for (; rlen >= rone; rlen -= rone, ranges += rone) { + offset = bus->map(addr, ranges, na, ns, pna); + if (offset != OF_BAD_ADDR) + break; + } + if (offset == OF_BAD_ADDR) { + debug("not found !\n"); + return 1; + } + memcpy(addr, ranges + na, 4 * pna); + + finish: + of_dump_addr("parent translation for:", addr, pna); + debug("with offset: %llx\n", (unsigned long long)offset); + + /* Translate it into parent bus space */ + return pbus->translate(addr, offset, pna); +} + +/* + * Translate an address from the device-tree into a CPU physical address, + * this walks up the tree and applies the various bus mappings on the + * way. + * + * Note: We consider that crossing any level with #size-cells == 0 to mean + * that translation is impossible (that is we are not dealing with a value + * that can be mapped to a cpu physical address). This is not really specified + * that way, but this is traditionally the way IBM at least do things + */ +static u64 __of_translate_address(const struct device_node *dev, + const __be32 *in_addr, const char *rprop) +{ + struct device_node *parent = NULL; + struct of_bus *bus, *pbus; + __be32 addr[OF_MAX_ADDR_CELLS]; + int na, ns, pna, pns; + u64 result = OF_BAD_ADDR; + + debug("** translation for device %s **\n", of_node_full_name(dev)); + + /* Increase refcount at current level */ + (void)of_node_get(dev); + + /* Get parent & match bus type */ + parent = of_get_parent(dev); + if (parent == NULL) + goto bail; + bus = of_match_bus(parent); + + /* Count address cells & copy address locally */ + bus->count_cells(dev, &na, &ns); + if (!OF_CHECK_COUNTS(na, ns)) { + debug("Bad cell count for %s\n", of_node_full_name(dev)); + goto bail; + } + memcpy(addr, in_addr, na * 4); + + debug("bus is %s (na=%d, ns=%d) on %s\n", bus->name, na, ns, + of_node_full_name(parent)); + of_dump_addr("translating address:", addr, na); + + /* Translate */ + for (;;) { + /* Switch to parent bus */ + of_node_put(dev); + dev = parent; + parent = of_get_parent(dev); + + /* If root, we have finished */ + if (parent == NULL) { + debug("reached root node\n"); + result = of_read_number(addr, na); + break; + } + + /* Get new parent bus and counts */ + pbus = of_match_bus(parent); + pbus->count_cells(dev, &pna, &pns); + if (!OF_CHECK_COUNTS(pna, pns)) { + debug("Bad cell count for %s\n", + of_node_full_name(dev)); + break; + } + + debug("parent bus is %s (na=%d, ns=%d) on %s\n", pbus->name, + pna, pns, of_node_full_name(parent)); + + /* Apply bus translation */ + if (of_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop)) + break; + + /* Complete the move up one level */ + na = pna; + ns = pns; + bus = pbus; + + of_dump_addr("one level translation:", addr, na); + } + bail: + of_node_put(parent); + of_node_put(dev); + + return result; +} + +u64 of_translate_address(const struct device_node *dev, const __be32 *in_addr) +{ + return __of_translate_address(dev, in_addr, "ranges"); +} + +u64 of_translate_dma_address(const struct device_node *dev, const __be32 *in_addr) +{ + return __of_translate_address(dev, in_addr, "dma-ranges"); +} + +int of_get_dma_range(const struct device_node *dev, phys_addr_t *cpu, + dma_addr_t *bus, u64 *size) +{ + bool found_dma_ranges = false; + struct device_node *parent; + struct of_bus *bus_node; + int na, ns, pna, pns; + const __be32 *ranges; + int ret = 0; + int len; + + /* Find the closest dma-ranges property */ + dev = of_node_get(dev); + while (dev) { + ranges = of_get_property(dev, "dma-ranges", &len); + + /* Ignore empty ranges, they imply no translation required */ + if (ranges && len > 0) + break; + + /* Once we find 'dma-ranges', then a missing one is an error */ + if (found_dma_ranges && !ranges) { + ret = -EINVAL; + goto out; + } + + if (ranges) + found_dma_ranges = true; + + parent = of_get_parent(dev); + of_node_put(dev); + dev = parent; + } + + if (!dev || !ranges) { + debug("no dma-ranges found for node %s\n", + of_node_full_name(dev)); + ret = -ENOENT; + goto out; + } + + /* switch to that node */ + parent = of_get_parent(dev); + if (!parent) { + printf("Found dma-ranges in root node, shoudln't happen\n"); + ret = -EINVAL; + goto out; + } + + /* Get the address sizes both for the bus and its parent */ + bus_node = of_match_bus((struct device_node*)dev); + bus_node->count_cells(dev, &na, &ns); + if (!OF_CHECK_COUNTS(na, ns)) { + printf("Bad cell count for %s\n", of_node_full_name(dev)); + ret = -EINVAL; + goto out_parent; + } + + bus_node = of_match_bus(parent); + bus_node->count_cells(parent, &pna, &pns); + if (!OF_CHECK_COUNTS(pna, pns)) { + printf("Bad cell count for %s\n", of_node_full_name(parent)); + ret = -EINVAL; + goto out_parent; + } + + *bus = of_read_number(ranges, na); + *cpu = of_translate_dma_address(dev, ranges + na); + *size = of_read_number(ranges + na + pna, ns); + +out_parent: + of_node_put(parent); +out: + of_node_put(dev); + return ret; +} + + +static int __of_address_to_resource(const struct device_node *dev, + const __be32 *addrp, u64 size, unsigned int flags, + const char *name, struct resource *r) +{ + u64 taddr; + + if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0) + return -EINVAL; + taddr = of_translate_address(dev, addrp); + if (taddr == OF_BAD_ADDR) + return -EINVAL; + memset(r, 0, sizeof(struct resource)); + r->start = taddr; + r->end = taddr + size - 1; + r->flags = flags; + r->name = name ? name : dev->full_name; + + return 0; +} + +int of_address_to_resource(const struct device_node *dev, int index, + struct resource *r) +{ + const __be32 *addrp; + u64 size; + unsigned int flags; + const char *name = NULL; + + addrp = of_get_address(dev, index, &size, &flags); + if (addrp == NULL) + return -EINVAL; + + /* Get optional "reg-names" property to add a name to a resource */ + of_property_read_string_index(dev, "reg-names", index, &name); + + return __of_address_to_resource(dev, addrp, size, flags, name, r); +} diff --git a/roms/u-boot/drivers/core/of_extra.c b/roms/u-boot/drivers/core/of_extra.c new file mode 100644 index 000000000..7702beff9 --- /dev/null +++ b/roms/u-boot/drivers/core/of_extra.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2017 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <log.h> +#include <linux/libfdt.h> +#include <dm/of_access.h> +#include <dm/of_extra.h> +#include <dm/ofnode.h> + +int ofnode_read_fmap_entry(ofnode node, struct fmap_entry *entry) +{ + const char *prop; + ofnode subnode; + + if (ofnode_read_u32(node, "image-pos", &entry->offset)) { + debug("Node '%s' has bad/missing 'image-pos' property\n", + ofnode_get_name(node)); + return log_msg_ret("image-pos", -ENOENT); + } + if (ofnode_read_u32(node, "size", &entry->length)) { + debug("Node '%s' has bad/missing 'size' property\n", + ofnode_get_name(node)); + return log_msg_ret("size", -ENOENT); + } + entry->used = ofnode_read_s32_default(node, "used", entry->length); + prop = ofnode_read_string(node, "compress"); + if (prop) { + if (!strcmp(prop, "lz4")) + entry->compress_algo = FMAP_COMPRESS_LZ4; + else + return log_msg_ret("compression algo", -EINVAL); + } else { + entry->compress_algo = FMAP_COMPRESS_NONE; + } + entry->unc_length = ofnode_read_s32_default(node, "uncomp-size", + entry->length); + subnode = ofnode_find_subnode(node, "hash"); + if (ofnode_valid(subnode)) { + prop = ofnode_read_prop(subnode, "value", &entry->hash_size); + + /* Assume it is sha256 */ + entry->hash_algo = prop ? FMAP_HASH_SHA256 : FMAP_HASH_NONE; + entry->hash = (uint8_t *)prop; + } + + return 0; +} + +int ofnode_decode_region(ofnode node, const char *prop_name, fdt_addr_t *basep, + fdt_size_t *sizep) +{ + const fdt_addr_t *cell; + int len; + + debug("%s: %s: %s\n", __func__, ofnode_get_name(node), prop_name); + cell = ofnode_get_property(node, prop_name, &len); + if (!cell || (len < sizeof(fdt_addr_t) * 2)) { + debug("cell=%p, len=%d\n", cell, len); + return -1; + } + + *basep = fdt_addr_to_cpu(*cell); + *sizep = fdt_size_to_cpu(cell[1]); + debug("%s: base=%08lx, size=%lx\n", __func__, (ulong)*basep, + (ulong)*sizep); + + return 0; +} + +int ofnode_decode_memory_region(ofnode config_node, const char *mem_type, + const char *suffix, fdt_addr_t *basep, + fdt_size_t *sizep) +{ + char prop_name[50]; + const char *mem; + fdt_size_t size, offset_size; + fdt_addr_t base, offset; + ofnode node; + + if (!ofnode_valid(config_node)) { + config_node = ofnode_path("/config"); + if (!ofnode_valid(config_node)) { + debug("%s: Cannot find /config node\n", __func__); + return -ENOENT; + } + } + if (!suffix) + suffix = ""; + + snprintf(prop_name, sizeof(prop_name), "%s-memory%s", mem_type, + suffix); + mem = ofnode_read_string(config_node, prop_name); + if (!mem) { + debug("%s: No memory type for '%s', using /memory\n", __func__, + prop_name); + mem = "/memory"; + } + + node = ofnode_path(mem); + if (!ofnode_valid(node)) { + debug("%s: Failed to find node '%s'\n", __func__, mem); + return -ENOENT; + } + + /* + * Not strictly correct - the memory may have multiple banks. We just + * use the first + */ + if (ofnode_decode_region(node, "reg", &base, &size)) { + debug("%s: Failed to decode memory region %s\n", __func__, + mem); + return -EINVAL; + } + + snprintf(prop_name, sizeof(prop_name), "%s-offset%s", mem_type, + suffix); + if (ofnode_decode_region(config_node, prop_name, &offset, + &offset_size)) { + debug("%s: Failed to decode memory region '%s'\n", __func__, + prop_name); + return -EINVAL; + } + + *basep = base + offset; + *sizep = offset_size; + + return 0; +} + +bool ofnode_phy_is_fixed_link(ofnode eth_node, ofnode *phy_node) +{ + ofnode node, subnode; + int len; + + subnode = ofnode_find_subnode(eth_node, "fixed-link"); + if (ofnode_valid(subnode)) { + /* new binding */ + node = subnode; + } else if (ofnode_get_property(eth_node, "fixed-link", &len) && + len == (5 * sizeof(__be32))) { + /* old binding */ + node = eth_node; + } else { + return false; + } + + if (phy_node) + *phy_node = node; + + return true; +} diff --git a/roms/u-boot/drivers/core/ofnode.c b/roms/u-boot/drivers/core/ofnode.c new file mode 100644 index 000000000..6c771e364 --- /dev/null +++ b/roms/u-boot/drivers/core/ofnode.c @@ -0,0 +1,1054 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2017 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <fdtdec.h> +#include <fdt_support.h> +#include <log.h> +#include <malloc.h> +#include <linux/libfdt.h> +#include <dm/of_access.h> +#include <dm/of_addr.h> +#include <dm/ofnode.h> +#include <linux/err.h> +#include <linux/ioport.h> +#include <asm/global_data.h> + +int ofnode_read_u32(ofnode node, const char *propname, u32 *outp) +{ + return ofnode_read_u32_index(node, propname, 0, outp); +} + +u32 ofnode_read_u32_default(ofnode node, const char *propname, u32 def) +{ + assert(ofnode_valid(node)); + ofnode_read_u32_index(node, propname, 0, &def); + + return def; +} + +int ofnode_read_u32_index(ofnode node, const char *propname, int index, + u32 *outp) +{ + const fdt32_t *cell; + int len; + + assert(ofnode_valid(node)); + debug("%s: %s: ", __func__, propname); + + if (ofnode_is_np(node)) + return of_read_u32_index(ofnode_to_np(node), propname, index, + outp); + + cell = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), propname, + &len); + if (!cell) { + debug("(not found)\n"); + return -EINVAL; + } + + if (len < (sizeof(int) * (index + 1))) { + debug("(not large enough)\n"); + return -EOVERFLOW; + } + + *outp = fdt32_to_cpu(cell[index]); + debug("%#x (%d)\n", *outp, *outp); + + return 0; +} + +u32 ofnode_read_u32_index_default(ofnode node, const char *propname, int index, + u32 def) +{ + assert(ofnode_valid(node)); + ofnode_read_u32_index(node, propname, index, &def); + + return def; +} + +int ofnode_read_s32_default(ofnode node, const char *propname, s32 def) +{ + assert(ofnode_valid(node)); + ofnode_read_u32(node, propname, (u32 *)&def); + + return def; +} + +int ofnode_read_u64(ofnode node, const char *propname, u64 *outp) +{ + const unaligned_fdt64_t *cell; + int len; + + assert(ofnode_valid(node)); + debug("%s: %s: ", __func__, propname); + + if (ofnode_is_np(node)) + return of_read_u64(ofnode_to_np(node), propname, outp); + + cell = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), propname, + &len); + if (!cell || len < sizeof(*cell)) { + debug("(not found)\n"); + return -EINVAL; + } + *outp = fdt64_to_cpu(cell[0]); + debug("%#llx (%lld)\n", (unsigned long long)*outp, + (unsigned long long)*outp); + + return 0; +} + +u64 ofnode_read_u64_default(ofnode node, const char *propname, u64 def) +{ + assert(ofnode_valid(node)); + ofnode_read_u64(node, propname, &def); + + return def; +} + +bool ofnode_read_bool(ofnode node, const char *propname) +{ + const void *prop; + + assert(ofnode_valid(node)); + debug("%s: %s: ", __func__, propname); + + prop = ofnode_get_property(node, propname, NULL); + + debug("%s\n", prop ? "true" : "false"); + + return prop ? true : false; +} + +const void *ofnode_read_prop(ofnode node, const char *propname, int *sizep) +{ + const char *val = NULL; + int len; + + assert(ofnode_valid(node)); + debug("%s: %s: ", __func__, propname); + + if (ofnode_is_np(node)) { + struct property *prop = of_find_property( + ofnode_to_np(node), propname, &len); + + if (prop) { + val = prop->value; + len = prop->length; + } + } else { + val = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), + propname, &len); + } + if (!val) { + debug("<not found>\n"); + if (sizep) + *sizep = -FDT_ERR_NOTFOUND; + return NULL; + } + if (sizep) + *sizep = len; + + return val; +} + +const char *ofnode_read_string(ofnode node, const char *propname) +{ + const char *str; + int len; + + str = ofnode_read_prop(node, propname, &len); + if (!str) + return NULL; + + if (strnlen(str, len) >= len) { + debug("<invalid>\n"); + return NULL; + } + debug("%s\n", str); + + return str; +} + +int ofnode_read_size(ofnode node, const char *propname) +{ + int len; + + if (!ofnode_read_prop(node, propname, &len)) + return -EINVAL; + + return len; +} + +ofnode ofnode_find_subnode(ofnode node, const char *subnode_name) +{ + ofnode subnode; + + assert(ofnode_valid(node)); + debug("%s: %s: ", __func__, subnode_name); + + if (ofnode_is_np(node)) { + const struct device_node *np = ofnode_to_np(node); + + for (np = np->child; np; np = np->sibling) { + if (!strcmp(subnode_name, np->name)) + break; + } + subnode = np_to_ofnode(np); + } else { + int ooffset = fdt_subnode_offset(gd->fdt_blob, + ofnode_to_offset(node), subnode_name); + subnode = offset_to_ofnode(ooffset); + } + debug("%s\n", ofnode_valid(subnode) ? + ofnode_get_name(subnode) : "<none>"); + + return subnode; +} + +int ofnode_read_u32_array(ofnode node, const char *propname, + u32 *out_values, size_t sz) +{ + assert(ofnode_valid(node)); + debug("%s: %s: ", __func__, propname); + + if (ofnode_is_np(node)) { + return of_read_u32_array(ofnode_to_np(node), propname, + out_values, sz); + } else { + return fdtdec_get_int_array(gd->fdt_blob, + ofnode_to_offset(node), propname, + out_values, sz); + } +} + +#if !CONFIG_IS_ENABLED(DM_INLINE_OFNODE) +bool ofnode_is_enabled(ofnode node) +{ + if (ofnode_is_np(node)) { + return of_device_is_available(ofnode_to_np(node)); + } else { + return fdtdec_get_is_enabled(gd->fdt_blob, + ofnode_to_offset(node)); + } +} + +ofnode ofnode_first_subnode(ofnode node) +{ + assert(ofnode_valid(node)); + if (ofnode_is_np(node)) + return np_to_ofnode(node.np->child); + + return offset_to_ofnode( + fdt_first_subnode(gd->fdt_blob, ofnode_to_offset(node))); +} + +ofnode ofnode_next_subnode(ofnode node) +{ + assert(ofnode_valid(node)); + if (ofnode_is_np(node)) + return np_to_ofnode(node.np->sibling); + + return offset_to_ofnode( + fdt_next_subnode(gd->fdt_blob, ofnode_to_offset(node))); +} +#endif /* !DM_INLINE_OFNODE */ + +ofnode ofnode_get_parent(ofnode node) +{ + ofnode parent; + + assert(ofnode_valid(node)); + if (ofnode_is_np(node)) + parent = np_to_ofnode(of_get_parent(ofnode_to_np(node))); + else + parent.of_offset = fdt_parent_offset(gd->fdt_blob, + ofnode_to_offset(node)); + + return parent; +} + +const char *ofnode_get_name(ofnode node) +{ + if (!ofnode_valid(node)) { + debug("%s node not valid\n", __func__); + return NULL; + } + + if (ofnode_is_np(node)) + return strrchr(node.np->full_name, '/') + 1; + + return fdt_get_name(gd->fdt_blob, ofnode_to_offset(node), NULL); +} + +ofnode ofnode_get_by_phandle(uint phandle) +{ + ofnode node; + + if (of_live_active()) + node = np_to_ofnode(of_find_node_by_phandle(phandle)); + else + node.of_offset = fdt_node_offset_by_phandle(gd->fdt_blob, + phandle); + + return node; +} + +fdt_addr_t ofnode_get_addr_size_index(ofnode node, int index, fdt_size_t *size) +{ + int na, ns; + + *size = FDT_SIZE_T_NONE; + + if (ofnode_is_np(node)) { + const __be32 *prop_val; + u64 size64; + uint flags; + + prop_val = of_get_address(ofnode_to_np(node), index, &size64, + &flags); + if (!prop_val) + return FDT_ADDR_T_NONE; + if (size) + *size = size64; + + ns = of_n_size_cells(ofnode_to_np(node)); + + if (IS_ENABLED(CONFIG_OF_TRANSLATE) && ns > 0) { + return of_translate_address(ofnode_to_np(node), prop_val); + } else { + na = of_n_addr_cells(ofnode_to_np(node)); + return of_read_number(prop_val, na); + } + } else { + na = ofnode_read_simple_addr_cells(ofnode_get_parent(node)); + ns = ofnode_read_simple_size_cells(ofnode_get_parent(node)); + return fdtdec_get_addr_size_fixed(gd->fdt_blob, + ofnode_to_offset(node), "reg", + index, na, ns, size, true); + } + + return FDT_ADDR_T_NONE; +} + +fdt_addr_t ofnode_get_addr_index(ofnode node, int index) +{ + fdt_size_t size; + + return ofnode_get_addr_size_index(node, index, &size); +} + +fdt_addr_t ofnode_get_addr(ofnode node) +{ + return ofnode_get_addr_index(node, 0); +} + +fdt_size_t ofnode_get_size(ofnode node) +{ + fdt_size_t size; + + ofnode_get_addr_size_index(node, 0, &size); + + return size; +} + +int ofnode_stringlist_search(ofnode node, const char *property, + const char *string) +{ + if (ofnode_is_np(node)) { + return of_property_match_string(ofnode_to_np(node), + property, string); + } else { + int ret; + + ret = fdt_stringlist_search(gd->fdt_blob, + ofnode_to_offset(node), property, + string); + if (ret == -FDT_ERR_NOTFOUND) + return -ENODATA; + else if (ret < 0) + return -EINVAL; + + return ret; + } +} + +int ofnode_read_string_index(ofnode node, const char *property, int index, + const char **outp) +{ + if (ofnode_is_np(node)) { + return of_property_read_string_index(ofnode_to_np(node), + property, index, outp); + } else { + int len; + + *outp = fdt_stringlist_get(gd->fdt_blob, ofnode_to_offset(node), + property, index, &len); + if (len < 0) + return -EINVAL; + return 0; + } +} + +int ofnode_read_string_count(ofnode node, const char *property) +{ + if (ofnode_is_np(node)) { + return of_property_count_strings(ofnode_to_np(node), property); + } else { + return fdt_stringlist_count(gd->fdt_blob, + ofnode_to_offset(node), property); + } +} + +static void ofnode_from_fdtdec_phandle_args(struct fdtdec_phandle_args *in, + struct ofnode_phandle_args *out) +{ + assert(OF_MAX_PHANDLE_ARGS == MAX_PHANDLE_ARGS); + out->node = offset_to_ofnode(in->node); + out->args_count = in->args_count; + memcpy(out->args, in->args, sizeof(out->args)); +} + +static void ofnode_from_of_phandle_args(struct of_phandle_args *in, + struct ofnode_phandle_args *out) +{ + assert(OF_MAX_PHANDLE_ARGS == MAX_PHANDLE_ARGS); + out->node = np_to_ofnode(in->np); + out->args_count = in->args_count; + memcpy(out->args, in->args, sizeof(out->args)); +} + +int ofnode_parse_phandle_with_args(ofnode node, const char *list_name, + const char *cells_name, int cell_count, + int index, + struct ofnode_phandle_args *out_args) +{ + if (ofnode_is_np(node)) { + struct of_phandle_args args; + int ret; + + ret = of_parse_phandle_with_args(ofnode_to_np(node), + list_name, cells_name, + cell_count, index, + &args); + if (ret) + return ret; + ofnode_from_of_phandle_args(&args, out_args); + } else { + struct fdtdec_phandle_args args; + int ret; + + ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, + ofnode_to_offset(node), + list_name, cells_name, + cell_count, index, &args); + if (ret) + return ret; + ofnode_from_fdtdec_phandle_args(&args, out_args); + } + + return 0; +} + +int ofnode_count_phandle_with_args(ofnode node, const char *list_name, + const char *cells_name, int cell_count) +{ + if (ofnode_is_np(node)) + return of_count_phandle_with_args(ofnode_to_np(node), + list_name, cells_name, cell_count); + else + return fdtdec_parse_phandle_with_args(gd->fdt_blob, + ofnode_to_offset(node), list_name, cells_name, + cell_count, -1, NULL); +} + +ofnode ofnode_path(const char *path) +{ + if (of_live_active()) + return np_to_ofnode(of_find_node_by_path(path)); + else + return offset_to_ofnode(fdt_path_offset(gd->fdt_blob, path)); +} + +const void *ofnode_read_chosen_prop(const char *propname, int *sizep) +{ + ofnode chosen_node; + + chosen_node = ofnode_path("/chosen"); + + return ofnode_read_prop(chosen_node, propname, sizep); +} + +const char *ofnode_read_chosen_string(const char *propname) +{ + return ofnode_read_chosen_prop(propname, NULL); +} + +ofnode ofnode_get_chosen_node(const char *name) +{ + const char *prop; + + prop = ofnode_read_chosen_prop(name, NULL); + if (!prop) + return ofnode_null(); + + return ofnode_path(prop); +} + +const void *ofnode_read_aliases_prop(const char *propname, int *sizep) +{ + ofnode node; + + node = ofnode_path("/aliases"); + + return ofnode_read_prop(node, propname, sizep); +} + +ofnode ofnode_get_aliases_node(const char *name) +{ + const char *prop; + + prop = ofnode_read_aliases_prop(name, NULL); + if (!prop) + return ofnode_null(); + + debug("%s: node_path: %s\n", __func__, prop); + + return ofnode_path(prop); +} + +int ofnode_get_child_count(ofnode parent) +{ + ofnode child; + int num = 0; + + ofnode_for_each_subnode(child, parent) + num++; + + return num; +} + +static int decode_timing_property(ofnode node, const char *name, + struct timing_entry *result) +{ + int length, ret = 0; + + length = ofnode_read_size(node, name); + if (length < 0) { + debug("%s: could not find property %s\n", + ofnode_get_name(node), name); + return length; + } + + if (length == sizeof(u32)) { + result->typ = ofnode_read_u32_default(node, name, 0); + result->min = result->typ; + result->max = result->typ; + } else { + ret = ofnode_read_u32_array(node, name, &result->min, 3); + } + + return ret; +} + +int ofnode_decode_display_timing(ofnode parent, int index, + struct display_timing *dt) +{ + int i; + ofnode timings, node; + u32 val = 0; + int ret = 0; + + timings = ofnode_find_subnode(parent, "display-timings"); + if (!ofnode_valid(timings)) + return -EINVAL; + + i = 0; + ofnode_for_each_subnode(node, timings) { + if (i++ == index) + break; + } + + if (!ofnode_valid(node)) + return -EINVAL; + + memset(dt, 0, sizeof(*dt)); + + ret |= decode_timing_property(node, "hback-porch", &dt->hback_porch); + ret |= decode_timing_property(node, "hfront-porch", &dt->hfront_porch); + ret |= decode_timing_property(node, "hactive", &dt->hactive); + ret |= decode_timing_property(node, "hsync-len", &dt->hsync_len); + ret |= decode_timing_property(node, "vback-porch", &dt->vback_porch); + ret |= decode_timing_property(node, "vfront-porch", &dt->vfront_porch); + ret |= decode_timing_property(node, "vactive", &dt->vactive); + ret |= decode_timing_property(node, "vsync-len", &dt->vsync_len); + ret |= decode_timing_property(node, "clock-frequency", &dt->pixelclock); + + dt->flags = 0; + val = ofnode_read_u32_default(node, "vsync-active", -1); + if (val != -1) { + dt->flags |= val ? DISPLAY_FLAGS_VSYNC_HIGH : + DISPLAY_FLAGS_VSYNC_LOW; + } + val = ofnode_read_u32_default(node, "hsync-active", -1); + if (val != -1) { + dt->flags |= val ? DISPLAY_FLAGS_HSYNC_HIGH : + DISPLAY_FLAGS_HSYNC_LOW; + } + val = ofnode_read_u32_default(node, "de-active", -1); + if (val != -1) { + dt->flags |= val ? DISPLAY_FLAGS_DE_HIGH : + DISPLAY_FLAGS_DE_LOW; + } + val = ofnode_read_u32_default(node, "pixelclk-active", -1); + if (val != -1) { + dt->flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE : + DISPLAY_FLAGS_PIXDATA_NEGEDGE; + } + + if (ofnode_read_bool(node, "interlaced")) + dt->flags |= DISPLAY_FLAGS_INTERLACED; + if (ofnode_read_bool(node, "doublescan")) + dt->flags |= DISPLAY_FLAGS_DOUBLESCAN; + if (ofnode_read_bool(node, "doubleclk")) + dt->flags |= DISPLAY_FLAGS_DOUBLECLK; + + return ret; +} + +const void *ofnode_get_property(ofnode node, const char *propname, int *lenp) +{ + if (ofnode_is_np(node)) + return of_get_property(ofnode_to_np(node), propname, lenp); + else + return fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), + propname, lenp); +} + +int ofnode_get_first_property(ofnode node, struct ofprop *prop) +{ + prop->node = node; + + if (ofnode_is_np(node)) { + prop->prop = of_get_first_property(ofnode_to_np(prop->node)); + if (!prop->prop) + return -FDT_ERR_NOTFOUND; + } else { + prop->offset = + fdt_first_property_offset(gd->fdt_blob, + ofnode_to_offset(prop->node)); + if (prop->offset < 0) + return prop->offset; + } + + return 0; +} + +int ofnode_get_next_property(struct ofprop *prop) +{ + if (ofnode_is_np(prop->node)) { + prop->prop = of_get_next_property(ofnode_to_np(prop->node), + prop->prop); + if (!prop->prop) + return -FDT_ERR_NOTFOUND; + } else { + prop->offset = fdt_next_property_offset(gd->fdt_blob, + prop->offset); + if (prop->offset < 0) + return prop->offset; + } + + return 0; +} + +const void *ofnode_get_property_by_prop(const struct ofprop *prop, + const char **propname, int *lenp) +{ + if (ofnode_is_np(prop->node)) + return of_get_property_by_prop(ofnode_to_np(prop->node), + prop->prop, propname, lenp); + else + return fdt_getprop_by_offset(gd->fdt_blob, + prop->offset, + propname, lenp); +} + +bool ofnode_is_available(ofnode node) +{ + if (ofnode_is_np(node)) + return of_device_is_available(ofnode_to_np(node)); + else + return fdtdec_get_is_enabled(gd->fdt_blob, + ofnode_to_offset(node)); +} + +fdt_addr_t ofnode_get_addr_size(ofnode node, const char *property, + fdt_size_t *sizep) +{ + if (ofnode_is_np(node)) { + int na, ns; + int psize; + const struct device_node *np = ofnode_to_np(node); + const __be32 *prop = of_get_property(np, property, &psize); + + if (!prop) + return FDT_ADDR_T_NONE; + na = of_n_addr_cells(np); + ns = of_n_size_cells(np); + *sizep = of_read_number(prop + na, ns); + + if (CONFIG_IS_ENABLED(OF_TRANSLATE) && ns > 0) + return of_translate_address(np, prop); + else + return of_read_number(prop, na); + } else { + return fdtdec_get_addr_size(gd->fdt_blob, + ofnode_to_offset(node), property, + sizep); + } +} + +const uint8_t *ofnode_read_u8_array_ptr(ofnode node, const char *propname, + size_t sz) +{ + if (ofnode_is_np(node)) { + const struct device_node *np = ofnode_to_np(node); + int psize; + const __be32 *prop = of_get_property(np, propname, &psize); + + if (!prop || sz != psize) + return NULL; + return (uint8_t *)prop; + + } else { + return fdtdec_locate_byte_array(gd->fdt_blob, + ofnode_to_offset(node), propname, sz); + } +} + +int ofnode_read_pci_addr(ofnode node, enum fdt_pci_space type, + const char *propname, struct fdt_pci_addr *addr) +{ + const fdt32_t *cell; + int len; + int ret = -ENOENT; + + debug("%s: %s: ", __func__, propname); + + /* + * If we follow the pci bus bindings strictly, we should check + * the value of the node's parent node's #address-cells and + * #size-cells. They need to be 3 and 2 accordingly. However, + * for simplicity we skip the check here. + */ + cell = ofnode_get_property(node, propname, &len); + if (!cell) + goto fail; + + if ((len % FDT_PCI_REG_SIZE) == 0) { + int num = len / FDT_PCI_REG_SIZE; + int i; + + for (i = 0; i < num; i++) { + debug("pci address #%d: %08lx %08lx %08lx\n", i, + (ulong)fdt32_to_cpu(cell[0]), + (ulong)fdt32_to_cpu(cell[1]), + (ulong)fdt32_to_cpu(cell[2])); + if ((fdt32_to_cpu(*cell) & type) == type) { + addr->phys_hi = fdt32_to_cpu(cell[0]); + addr->phys_mid = fdt32_to_cpu(cell[1]); + addr->phys_lo = fdt32_to_cpu(cell[2]); + break; + } + + cell += (FDT_PCI_ADDR_CELLS + + FDT_PCI_SIZE_CELLS); + } + + if (i == num) { + ret = -ENXIO; + goto fail; + } + + return 0; + } + + ret = -EINVAL; + +fail: + debug("(not found)\n"); + return ret; +} + +int ofnode_read_pci_vendev(ofnode node, u16 *vendor, u16 *device) +{ + const char *list, *end; + int len; + + list = ofnode_get_property(node, "compatible", &len); + if (!list) + return -ENOENT; + + end = list + len; + while (list < end) { + len = strlen(list); + if (len >= strlen("pciVVVV,DDDD")) { + char *s = strstr(list, "pci"); + + /* + * check if the string is something like pciVVVV,DDDD.RR + * or just pciVVVV,DDDD + */ + if (s && s[7] == ',' && + (s[12] == '.' || s[12] == 0)) { + s += 3; + *vendor = simple_strtol(s, NULL, 16); + + s += 5; + *device = simple_strtol(s, NULL, 16); + + return 0; + } + } + list += (len + 1); + } + + return -ENOENT; +} + +int ofnode_read_addr_cells(ofnode node) +{ + if (ofnode_is_np(node)) { + return of_n_addr_cells(ofnode_to_np(node)); + } else { + int parent = fdt_parent_offset(gd->fdt_blob, + ofnode_to_offset(node)); + + return fdt_address_cells(gd->fdt_blob, parent); + } +} + +int ofnode_read_size_cells(ofnode node) +{ + if (ofnode_is_np(node)) { + return of_n_size_cells(ofnode_to_np(node)); + } else { + int parent = fdt_parent_offset(gd->fdt_blob, + ofnode_to_offset(node)); + + return fdt_size_cells(gd->fdt_blob, parent); + } +} + +int ofnode_read_simple_addr_cells(ofnode node) +{ + if (ofnode_is_np(node)) + return of_simple_addr_cells(ofnode_to_np(node)); + else + return fdt_address_cells(gd->fdt_blob, ofnode_to_offset(node)); +} + +int ofnode_read_simple_size_cells(ofnode node) +{ + if (ofnode_is_np(node)) + return of_simple_size_cells(ofnode_to_np(node)); + else + return fdt_size_cells(gd->fdt_blob, ofnode_to_offset(node)); +} + +bool ofnode_pre_reloc(ofnode node) +{ +#if defined(CONFIG_SPL_BUILD) || defined(CONFIG_TPL_BUILD) + /* for SPL and TPL the remaining nodes after the fdtgrep 1st pass + * had property dm-pre-reloc or u-boot,dm-spl/tpl. + * They are removed in final dtb (fdtgrep 2nd pass) + */ + return true; +#else + if (ofnode_read_bool(node, "u-boot,dm-pre-reloc")) + return true; + if (ofnode_read_bool(node, "u-boot,dm-pre-proper")) + return true; + + /* + * In regular builds individual spl and tpl handling both + * count as handled pre-relocation for later second init. + */ + if (ofnode_read_bool(node, "u-boot,dm-spl") || + ofnode_read_bool(node, "u-boot,dm-tpl")) + return true; + + return false; +#endif +} + +int ofnode_read_resource(ofnode node, uint index, struct resource *res) +{ + if (ofnode_is_np(node)) { + return of_address_to_resource(ofnode_to_np(node), index, res); + } else { + struct fdt_resource fres; + int ret; + + ret = fdt_get_resource(gd->fdt_blob, ofnode_to_offset(node), + "reg", index, &fres); + if (ret < 0) + return -EINVAL; + memset(res, '\0', sizeof(*res)); + res->start = fres.start; + res->end = fres.end; + + return 0; + } +} + +int ofnode_read_resource_byname(ofnode node, const char *name, + struct resource *res) +{ + int index; + + index = ofnode_stringlist_search(node, "reg-names", name); + if (index < 0) + return index; + + return ofnode_read_resource(node, index, res); +} + +u64 ofnode_translate_address(ofnode node, const fdt32_t *in_addr) +{ + if (ofnode_is_np(node)) + return of_translate_address(ofnode_to_np(node), in_addr); + else + return fdt_translate_address(gd->fdt_blob, ofnode_to_offset(node), in_addr); +} + +u64 ofnode_translate_dma_address(ofnode node, const fdt32_t *in_addr) +{ + if (ofnode_is_np(node)) + return of_translate_dma_address(ofnode_to_np(node), in_addr); + else + return fdt_translate_dma_address(gd->fdt_blob, ofnode_to_offset(node), in_addr); +} + +int ofnode_get_dma_range(ofnode node, phys_addr_t *cpu, dma_addr_t *bus, u64 *size) +{ + if (ofnode_is_np(node)) + return of_get_dma_range(ofnode_to_np(node), cpu, bus, size); + else + return fdt_get_dma_range(gd->fdt_blob, ofnode_to_offset(node), + cpu, bus, size); +} + +int ofnode_device_is_compatible(ofnode node, const char *compat) +{ + if (ofnode_is_np(node)) + return of_device_is_compatible(ofnode_to_np(node), compat, + NULL, NULL); + else + return !fdt_node_check_compatible(gd->fdt_blob, + ofnode_to_offset(node), + compat); +} + +ofnode ofnode_by_compatible(ofnode from, const char *compat) +{ + if (of_live_active()) { + return np_to_ofnode(of_find_compatible_node( + (struct device_node *)ofnode_to_np(from), NULL, + compat)); + } else { + return offset_to_ofnode(fdt_node_offset_by_compatible( + gd->fdt_blob, ofnode_to_offset(from), compat)); + } +} + +ofnode ofnode_by_prop_value(ofnode from, const char *propname, + const void *propval, int proplen) +{ + if (of_live_active()) { + return np_to_ofnode(of_find_node_by_prop_value( + (struct device_node *)ofnode_to_np(from), propname, + propval, proplen)); + } else { + return offset_to_ofnode(fdt_node_offset_by_prop_value( + gd->fdt_blob, ofnode_to_offset(from), + propname, propval, proplen)); + } +} + +int ofnode_write_prop(ofnode node, const char *propname, int len, + const void *value) +{ + const struct device_node *np = ofnode_to_np(node); + struct property *pp; + struct property *pp_last = NULL; + struct property *new; + + if (!of_live_active()) + return -ENOSYS; + + if (!np) + return -EINVAL; + + for (pp = np->properties; pp; pp = pp->next) { + if (strcmp(pp->name, propname) == 0) { + /* Property exists -> change value */ + pp->value = (void *)value; + pp->length = len; + return 0; + } + pp_last = pp; + } + + if (!pp_last) + return -ENOENT; + + /* Property does not exist -> append new property */ + new = malloc(sizeof(struct property)); + if (!new) + return -ENOMEM; + + new->name = strdup(propname); + if (!new->name) { + free(new); + return -ENOMEM; + } + + new->value = (void *)value; + new->length = len; + new->next = NULL; + + pp_last->next = new; + + return 0; +} + +int ofnode_write_string(ofnode node, const char *propname, const char *value) +{ + if (!of_live_active()) + return -ENOSYS; + + assert(ofnode_valid(node)); + + debug("%s: %s = %s", __func__, propname, value); + + return ofnode_write_prop(node, propname, strlen(value) + 1, value); +} + +int ofnode_set_enabled(ofnode node, bool value) +{ + if (!of_live_active()) + return -ENOSYS; + + assert(ofnode_valid(node)); + + if (value) + return ofnode_write_string(node, "status", "okay"); + else + return ofnode_write_string(node, "status", "disabled"); +} diff --git a/roms/u-boot/drivers/core/read.c b/roms/u-boot/drivers/core/read.c new file mode 100644 index 000000000..4307ca457 --- /dev/null +++ b/roms/u-boot/drivers/core/read.c @@ -0,0 +1,394 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2017 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <dm/of_access.h> +#include <mapmem.h> +#include <asm/global_data.h> +#include <asm/types.h> +#include <asm/io.h> +#include <linux/ioport.h> + +int dev_read_u32(const struct udevice *dev, const char *propname, u32 *outp) +{ + return ofnode_read_u32(dev_ofnode(dev), propname, outp); +} + +int dev_read_u32_default(const struct udevice *dev, const char *propname, + int def) +{ + return ofnode_read_u32_default(dev_ofnode(dev), propname, def); +} + +int dev_read_u32_index(struct udevice *dev, const char *propname, int index, + u32 *outp) +{ + return ofnode_read_u32_index(dev_ofnode(dev), propname, index, outp); +} + +u32 dev_read_u32_index_default(struct udevice *dev, const char *propname, + int index, u32 def) +{ + return ofnode_read_u32_index_default(dev_ofnode(dev), propname, index, + def); +} + +int dev_read_s32(const struct udevice *dev, const char *propname, s32 *outp) +{ + return ofnode_read_u32(dev_ofnode(dev), propname, (u32 *)outp); +} + +int dev_read_s32_default(const struct udevice *dev, const char *propname, + int def) +{ + return ofnode_read_u32_default(dev_ofnode(dev), propname, def); +} + +int dev_read_u32u(const struct udevice *dev, const char *propname, uint *outp) +{ + u32 val; + int ret; + + ret = ofnode_read_u32(dev_ofnode(dev), propname, &val); + if (ret) + return ret; + *outp = val; + + return 0; +} + +int dev_read_u64(const struct udevice *dev, const char *propname, u64 *outp) +{ + return ofnode_read_u64(dev_ofnode(dev), propname, outp); +} + +u64 dev_read_u64_default(const struct udevice *dev, const char *propname, + u64 def) +{ + return ofnode_read_u64_default(dev_ofnode(dev), propname, def); +} + +const char *dev_read_string(const struct udevice *dev, const char *propname) +{ + return ofnode_read_string(dev_ofnode(dev), propname); +} + +bool dev_read_bool(const struct udevice *dev, const char *propname) +{ + return ofnode_read_bool(dev_ofnode(dev), propname); +} + +ofnode dev_read_subnode(const struct udevice *dev, const char *subnode_name) +{ + return ofnode_find_subnode(dev_ofnode(dev), subnode_name); +} + +ofnode dev_read_first_subnode(const struct udevice *dev) +{ + return ofnode_first_subnode(dev_ofnode(dev)); +} + +ofnode dev_read_next_subnode(ofnode node) +{ + return ofnode_next_subnode(node); +} + +int dev_read_size(const struct udevice *dev, const char *propname) +{ + return ofnode_read_size(dev_ofnode(dev), propname); +} + +fdt_addr_t dev_read_addr_index(const struct udevice *dev, int index) +{ + if (ofnode_is_np(dev_ofnode(dev))) + return ofnode_get_addr_index(dev_ofnode(dev), index); + else + return devfdt_get_addr_index(dev, index); +} + +fdt_addr_t dev_read_addr_size_index(const struct udevice *dev, int index, + fdt_size_t *size) +{ + if (ofnode_is_np(dev_ofnode(dev))) + return ofnode_get_addr_size_index(dev_ofnode(dev), index, size); + else + return devfdt_get_addr_size_index(dev, index, size); +} + +void *dev_remap_addr_index(const struct udevice *dev, int index) +{ + fdt_addr_t addr = dev_read_addr_index(dev, index); + + if (addr == FDT_ADDR_T_NONE) + return NULL; + + return map_physmem(addr, 0, MAP_NOCACHE); +} + +fdt_addr_t dev_read_addr_name(const struct udevice *dev, const char *name) +{ + int index = dev_read_stringlist_search(dev, "reg-names", name); + + if (index < 0) + return FDT_ADDR_T_NONE; + else + return dev_read_addr_index(dev, index); +} + +fdt_addr_t dev_read_addr_size_name(const struct udevice *dev, const char *name, + fdt_size_t *size) +{ + int index = dev_read_stringlist_search(dev, "reg-names", name); + + if (index < 0) + return FDT_ADDR_T_NONE; + else + return dev_read_addr_size_index(dev, index, size); +} + +void *dev_remap_addr_name(const struct udevice *dev, const char *name) +{ + fdt_addr_t addr = dev_read_addr_name(dev, name); + + if (addr == FDT_ADDR_T_NONE) + return NULL; + + return map_physmem(addr, 0, MAP_NOCACHE); +} + +fdt_addr_t dev_read_addr(const struct udevice *dev) +{ + return dev_read_addr_index(dev, 0); +} + +void *dev_read_addr_ptr(const struct udevice *dev) +{ + fdt_addr_t addr = dev_read_addr(dev); + + return (addr == FDT_ADDR_T_NONE) ? NULL : (void *)(uintptr_t)addr; +} + +void *dev_remap_addr(const struct udevice *dev) +{ + return dev_remap_addr_index(dev, 0); +} + +fdt_addr_t dev_read_addr_size(const struct udevice *dev, const char *property, + fdt_size_t *sizep) +{ + return ofnode_get_addr_size(dev_ofnode(dev), property, sizep); +} + +const char *dev_read_name(const struct udevice *dev) +{ + return ofnode_get_name(dev_ofnode(dev)); +} + +int dev_read_stringlist_search(const struct udevice *dev, const char *property, + const char *string) +{ + return ofnode_stringlist_search(dev_ofnode(dev), property, string); +} + +int dev_read_string_index(const struct udevice *dev, const char *propname, + int index, const char **outp) +{ + return ofnode_read_string_index(dev_ofnode(dev), propname, index, outp); +} + +int dev_read_string_count(const struct udevice *dev, const char *propname) +{ + return ofnode_read_string_count(dev_ofnode(dev), propname); +} + +int dev_read_phandle_with_args(const struct udevice *dev, const char *list_name, + const char *cells_name, int cell_count, + int index, struct ofnode_phandle_args *out_args) +{ + return ofnode_parse_phandle_with_args(dev_ofnode(dev), list_name, + cells_name, cell_count, index, + out_args); +} + +int dev_count_phandle_with_args(const struct udevice *dev, + const char *list_name, const char *cells_name, + int cell_count) +{ + return ofnode_count_phandle_with_args(dev_ofnode(dev), list_name, + cells_name, cell_count); +} + +int dev_read_addr_cells(const struct udevice *dev) +{ + return ofnode_read_addr_cells(dev_ofnode(dev)); +} + +int dev_read_size_cells(const struct udevice *dev) +{ + return ofnode_read_size_cells(dev_ofnode(dev)); +} + +int dev_read_simple_addr_cells(const struct udevice *dev) +{ + return ofnode_read_simple_addr_cells(dev_ofnode(dev)); +} + +int dev_read_simple_size_cells(const struct udevice *dev) +{ + return ofnode_read_simple_size_cells(dev_ofnode(dev)); +} + +int dev_read_phandle(const struct udevice *dev) +{ + ofnode node = dev_ofnode(dev); + + if (ofnode_is_np(node)) + return ofnode_to_np(node)->phandle; + else + return fdt_get_phandle(gd->fdt_blob, ofnode_to_offset(node)); +} + +const void *dev_read_prop(const struct udevice *dev, const char *propname, + int *lenp) +{ + return ofnode_get_property(dev_ofnode(dev), propname, lenp); +} + +int dev_read_first_prop(const struct udevice *dev, struct ofprop *prop) +{ + return ofnode_get_first_property(dev_ofnode(dev), prop); +} + +int dev_read_next_prop(struct ofprop *prop) +{ + return ofnode_get_next_property(prop); +} + +const void *dev_read_prop_by_prop(struct ofprop *prop, + const char **propname, int *lenp) +{ + return ofnode_get_property_by_prop(prop, propname, lenp); +} + +int dev_read_alias_seq(const struct udevice *dev, int *devnump) +{ + ofnode node = dev_ofnode(dev); + const char *uc_name = dev->uclass->uc_drv->name; + int ret = -ENOTSUPP; + + if (ofnode_is_np(node)) { + ret = of_alias_get_id(ofnode_to_np(node), uc_name); + if (ret >= 0) { + *devnump = ret; + ret = 0; + } + } else { +#if CONFIG_IS_ENABLED(OF_CONTROL) + ret = fdtdec_get_alias_seq(gd->fdt_blob, uc_name, + ofnode_to_offset(node), devnump); +#endif + } + + return ret; +} + +int dev_read_u32_array(const struct udevice *dev, const char *propname, + u32 *out_values, size_t sz) +{ + return ofnode_read_u32_array(dev_ofnode(dev), propname, out_values, sz); +} + +const uint8_t *dev_read_u8_array_ptr(const struct udevice *dev, + const char *propname, size_t sz) +{ + return ofnode_read_u8_array_ptr(dev_ofnode(dev), propname, sz); +} + +int dev_read_enabled(const struct udevice *dev) +{ + ofnode node = dev_ofnode(dev); + + if (ofnode_is_np(node)) + return of_device_is_available(ofnode_to_np(node)); + else + return fdtdec_get_is_enabled(gd->fdt_blob, + ofnode_to_offset(node)); +} + +int dev_read_resource(const struct udevice *dev, uint index, + struct resource *res) +{ + return ofnode_read_resource(dev_ofnode(dev), index, res); +} + +int dev_read_resource_byname(const struct udevice *dev, const char *name, + struct resource *res) +{ + return ofnode_read_resource_byname(dev_ofnode(dev), name, res); +} + +u64 dev_translate_address(const struct udevice *dev, const fdt32_t *in_addr) +{ + return ofnode_translate_address(dev_ofnode(dev), in_addr); +} + +u64 dev_translate_dma_address(const struct udevice *dev, const fdt32_t *in_addr) +{ + return ofnode_translate_dma_address(dev_ofnode(dev), in_addr); +} + +int dev_get_dma_range(const struct udevice *dev, phys_addr_t *cpu, + dma_addr_t *bus, u64 *size) +{ + return ofnode_get_dma_range(dev_ofnode(dev), cpu, bus, size); +} + +int dev_read_alias_highest_id(const char *stem) +{ + if (of_live_active()) + return of_alias_get_highest_id(stem); + + return fdtdec_get_alias_highest_id(gd->fdt_blob, stem); +} + +fdt_addr_t dev_read_addr_pci(const struct udevice *dev) +{ + ulong addr; + + addr = dev_read_addr(dev); + if (addr == FDT_ADDR_T_NONE && !of_live_active()) + addr = devfdt_get_addr_pci(dev); + + return addr; +} + +int dev_get_child_count(const struct udevice *dev) +{ + return ofnode_get_child_count(dev_ofnode(dev)); +} + +int dev_read_pci_bus_range(const struct udevice *dev, + struct resource *res) +{ + const u32 *values; + int len; + + values = dev_read_prop(dev, "bus-range", &len); + if (!values || len < sizeof(*values) * 2) + return -EINVAL; + + res->start = *values++; + res->end = *values; + + return 0; +} + +int dev_decode_display_timing(const struct udevice *dev, int index, + struct display_timing *config) +{ + return ofnode_decode_display_timing(dev_ofnode(dev), index, config); +} diff --git a/roms/u-boot/drivers/core/read_extra.c b/roms/u-boot/drivers/core/read_extra.c new file mode 100644 index 000000000..513834882 --- /dev/null +++ b/roms/u-boot/drivers/core/read_extra.c @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2017 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <dm/of_addr.h> +#include <dm/read.h> +#include <linux/ioport.h> + +/* This file can hold non-inlined dev_read_...() functions */ diff --git a/roms/u-boot/drivers/core/regmap.c b/roms/u-boot/drivers/core/regmap.c new file mode 100644 index 000000000..3206f3d11 --- /dev/null +++ b/roms/u-boot/drivers/core/regmap.c @@ -0,0 +1,688 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <log.h> +#include <asm/global_data.h> +#include <linux/libfdt.h> +#include <malloc.h> +#include <mapmem.h> +#include <regmap.h> +#include <asm/io.h> +#include <dm/of_addr.h> +#include <dm/devres.h> +#include <linux/ioport.h> +#include <linux/compat.h> +#include <linux/err.h> +#include <linux/bitops.h> + +/* + * Internal representation of a regmap field. Instead of storing the MSB and + * LSB, store the shift and mask. This makes the code a bit cleaner and faster + * because the shift and mask don't have to be calculated every time. + */ +struct regmap_field { + struct regmap *regmap; + unsigned int mask; + /* lsb */ + unsigned int shift; + unsigned int reg; +}; + +DECLARE_GLOBAL_DATA_PTR; + +/** + * regmap_alloc() - Allocate a regmap with a given number of ranges. + * + * @count: Number of ranges to be allocated for the regmap. + * + * The default regmap width is set to REGMAP_SIZE_32. Callers can override it + * if they need. + * + * Return: A pointer to the newly allocated regmap, or NULL on error. + */ +static struct regmap *regmap_alloc(int count) +{ + struct regmap *map; + size_t size = sizeof(*map) + sizeof(map->ranges[0]) * count; + + map = calloc(1, size); + if (!map) + return NULL; + map->range_count = count; + map->width = REGMAP_SIZE_32; + + return map; +} + +#if CONFIG_IS_ENABLED(OF_PLATDATA) +int regmap_init_mem_plat(struct udevice *dev, fdt_val_t *reg, int count, + struct regmap **mapp) +{ + struct regmap_range *range; + struct regmap *map; + + map = regmap_alloc(count); + if (!map) + return -ENOMEM; + + for (range = map->ranges; count > 0; reg += 2, range++, count--) { + range->start = *reg; + range->size = reg[1]; + } + + *mapp = map; + + return 0; +} +#else +/** + * init_range() - Initialize a single range of a regmap + * @node: Device node that will use the map in question + * @range: Pointer to a regmap_range structure that will be initialized + * @addr_len: The length of the addr parts of the reg property + * @size_len: The length of the size parts of the reg property + * @index: The index of the range to initialize + * + * This function will read the necessary 'reg' information from the device tree + * (the 'addr' part, and the 'length' part), and initialize the range in + * quesion. + * + * Return: 0 if OK, -ve on error + */ +static int init_range(ofnode node, struct regmap_range *range, int addr_len, + int size_len, int index) +{ + fdt_size_t sz; + struct resource r; + + if (of_live_active()) { + int ret; + + ret = of_address_to_resource(ofnode_to_np(node), + index, &r); + if (ret) { + debug("%s: Could not read resource of range %d (ret = %d)\n", + ofnode_get_name(node), index, ret); + return ret; + } + + range->start = r.start; + range->size = r.end - r.start + 1; + } else { + int offset = ofnode_to_offset(node); + + range->start = fdtdec_get_addr_size_fixed(gd->fdt_blob, offset, + "reg", index, + addr_len, size_len, + &sz, true); + if (range->start == FDT_ADDR_T_NONE) { + debug("%s: Could not read start of range %d\n", + ofnode_get_name(node), index); + return -EINVAL; + } + + range->size = sz; + } + + return 0; +} + +int regmap_init_mem_index(ofnode node, struct regmap **mapp, int index) +{ + struct regmap *map; + int addr_len, size_len; + int ret; + + addr_len = ofnode_read_simple_addr_cells(ofnode_get_parent(node)); + if (addr_len < 0) { + debug("%s: Error while reading the addr length (ret = %d)\n", + ofnode_get_name(node), addr_len); + return addr_len; + } + + size_len = ofnode_read_simple_size_cells(ofnode_get_parent(node)); + if (size_len < 0) { + debug("%s: Error while reading the size length: (ret = %d)\n", + ofnode_get_name(node), size_len); + return size_len; + } + + map = regmap_alloc(1); + if (!map) + return -ENOMEM; + + ret = init_range(node, map->ranges, addr_len, size_len, index); + if (ret) + goto err; + + if (ofnode_read_bool(node, "little-endian")) + map->endianness = REGMAP_LITTLE_ENDIAN; + else if (ofnode_read_bool(node, "big-endian")) + map->endianness = REGMAP_BIG_ENDIAN; + else if (ofnode_read_bool(node, "native-endian")) + map->endianness = REGMAP_NATIVE_ENDIAN; + else /* Default: native endianness */ + map->endianness = REGMAP_NATIVE_ENDIAN; + + *mapp = map; + + return 0; +err: + regmap_uninit(map); + + return ret; +} + +int regmap_init_mem_range(ofnode node, ulong r_start, ulong r_size, + struct regmap **mapp) +{ + struct regmap *map; + struct regmap_range *range; + + map = regmap_alloc(1); + if (!map) + return -ENOMEM; + + range = &map->ranges[0]; + range->start = r_start; + range->size = r_size; + + if (ofnode_read_bool(node, "little-endian")) + map->endianness = REGMAP_LITTLE_ENDIAN; + else if (ofnode_read_bool(node, "big-endian")) + map->endianness = REGMAP_BIG_ENDIAN; + else if (ofnode_read_bool(node, "native-endian")) + map->endianness = REGMAP_NATIVE_ENDIAN; + else /* Default: native endianness */ + map->endianness = REGMAP_NATIVE_ENDIAN; + + *mapp = map; + return 0; +} + +int regmap_init_mem(ofnode node, struct regmap **mapp) +{ + struct regmap_range *range; + struct regmap *map; + int count; + int addr_len, size_len, both_len; + int len; + int index; + int ret; + + addr_len = ofnode_read_simple_addr_cells(ofnode_get_parent(node)); + if (addr_len < 0) { + debug("%s: Error while reading the addr length (ret = %d)\n", + ofnode_get_name(node), addr_len); + return addr_len; + } + + size_len = ofnode_read_simple_size_cells(ofnode_get_parent(node)); + if (size_len < 0) { + debug("%s: Error while reading the size length: (ret = %d)\n", + ofnode_get_name(node), size_len); + return size_len; + } + + both_len = addr_len + size_len; + if (!both_len) { + debug("%s: Both addr and size length are zero\n", + ofnode_get_name(node)); + return -EINVAL; + } + + len = ofnode_read_size(node, "reg"); + if (len < 0) { + debug("%s: Error while reading reg size (ret = %d)\n", + ofnode_get_name(node), len); + return len; + } + len /= sizeof(fdt32_t); + count = len / both_len; + if (!count) { + debug("%s: Not enough data in reg property\n", + ofnode_get_name(node)); + return -EINVAL; + } + + map = regmap_alloc(count); + if (!map) + return -ENOMEM; + + for (range = map->ranges, index = 0; count > 0; + count--, range++, index++) { + ret = init_range(node, range, addr_len, size_len, index); + if (ret) + goto err; + } + + if (ofnode_read_bool(node, "little-endian")) + map->endianness = REGMAP_LITTLE_ENDIAN; + else if (ofnode_read_bool(node, "big-endian")) + map->endianness = REGMAP_BIG_ENDIAN; + else if (ofnode_read_bool(node, "native-endian")) + map->endianness = REGMAP_NATIVE_ENDIAN; + else /* Default: native endianness */ + map->endianness = REGMAP_NATIVE_ENDIAN; + + *mapp = map; + + return 0; +err: + regmap_uninit(map); + + return ret; +} + +static void devm_regmap_release(struct udevice *dev, void *res) +{ + regmap_uninit(*(struct regmap **)res); +} + +struct regmap *devm_regmap_init(struct udevice *dev, + const struct regmap_bus *bus, + void *bus_context, + const struct regmap_config *config) +{ + int rc; + struct regmap **mapp, *map; + + mapp = devres_alloc(devm_regmap_release, sizeof(struct regmap *), + __GFP_ZERO); + if (unlikely(!mapp)) + return ERR_PTR(-ENOMEM); + + if (config && config->r_size != 0) + rc = regmap_init_mem_range(dev_ofnode(dev), config->r_start, + config->r_size, mapp); + else + rc = regmap_init_mem(dev_ofnode(dev), mapp); + if (rc) + return ERR_PTR(rc); + + map = *mapp; + if (config) { + map->width = config->width; + map->reg_offset_shift = config->reg_offset_shift; + } + + devres_add(dev, mapp); + return *mapp; +} +#endif + +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->ranges[range_num]; + + return map_sysmem(range->start, range->size); +} + +int regmap_uninit(struct regmap *map) +{ + free(map); + + return 0; +} + +static inline u8 __read_8(u8 *addr, enum regmap_endianness_t endianness) +{ + return readb(addr); +} + +static inline u16 __read_16(u16 *addr, enum regmap_endianness_t endianness) +{ + switch (endianness) { + case REGMAP_LITTLE_ENDIAN: + return in_le16(addr); + case REGMAP_BIG_ENDIAN: + return in_be16(addr); + case REGMAP_NATIVE_ENDIAN: + return readw(addr); + } + + return readw(addr); +} + +static inline u32 __read_32(u32 *addr, enum regmap_endianness_t endianness) +{ + switch (endianness) { + case REGMAP_LITTLE_ENDIAN: + return in_le32(addr); + case REGMAP_BIG_ENDIAN: + return in_be32(addr); + case REGMAP_NATIVE_ENDIAN: + return readl(addr); + } + + return readl(addr); +} + +#if defined(in_le64) && defined(in_be64) && defined(readq) +static inline u64 __read_64(u64 *addr, enum regmap_endianness_t endianness) +{ + switch (endianness) { + case REGMAP_LITTLE_ENDIAN: + return in_le64(addr); + case REGMAP_BIG_ENDIAN: + return in_be64(addr); + case REGMAP_NATIVE_ENDIAN: + return readq(addr); + } + + return readq(addr); +} +#endif + +int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset, + void *valp, size_t val_len) +{ + struct regmap_range *range; + void *ptr; + + if (range_num >= map->range_count) { + debug("%s: range index %d larger than range count\n", + __func__, range_num); + return -ERANGE; + } + range = &map->ranges[range_num]; + + offset <<= map->reg_offset_shift; + if (offset + val_len > range->size) { + debug("%s: offset/size combination invalid\n", __func__); + return -ERANGE; + } + + ptr = map_physmem(range->start + offset, val_len, MAP_NOCACHE); + + switch (val_len) { + case REGMAP_SIZE_8: + *((u8 *)valp) = __read_8(ptr, map->endianness); + break; + case REGMAP_SIZE_16: + *((u16 *)valp) = __read_16(ptr, map->endianness); + break; + case REGMAP_SIZE_32: + *((u32 *)valp) = __read_32(ptr, map->endianness); + break; +#if defined(in_le64) && defined(in_be64) && defined(readq) + case REGMAP_SIZE_64: + *((u64 *)valp) = __read_64(ptr, map->endianness); + break; +#endif + default: + debug("%s: regmap size %zu unknown\n", __func__, val_len); + return -EINVAL; + } + + return 0; +} + +int regmap_raw_read(struct regmap *map, uint offset, void *valp, size_t val_len) +{ + return regmap_raw_read_range(map, 0, offset, valp, val_len); +} + +int regmap_read(struct regmap *map, uint offset, uint *valp) +{ + union { + u8 v8; + u16 v16; + u32 v32; + u64 v64; + } u; + int res; + + res = regmap_raw_read(map, offset, &u, map->width); + if (res) + return res; + + switch (map->width) { + case REGMAP_SIZE_8: + *valp = u.v8; + break; + case REGMAP_SIZE_16: + *valp = u.v16; + break; + case REGMAP_SIZE_32: + *valp = u.v32; + break; + case REGMAP_SIZE_64: + *valp = u.v64; + break; + default: + unreachable(); + } + + return 0; +} + +static inline void __write_8(u8 *addr, const u8 *val, + enum regmap_endianness_t endianness) +{ + writeb(*val, addr); +} + +static inline void __write_16(u16 *addr, const u16 *val, + enum regmap_endianness_t endianness) +{ + switch (endianness) { + case REGMAP_NATIVE_ENDIAN: + writew(*val, addr); + break; + case REGMAP_LITTLE_ENDIAN: + out_le16(addr, *val); + break; + case REGMAP_BIG_ENDIAN: + out_be16(addr, *val); + break; + } +} + +static inline void __write_32(u32 *addr, const u32 *val, + enum regmap_endianness_t endianness) +{ + switch (endianness) { + case REGMAP_NATIVE_ENDIAN: + writel(*val, addr); + break; + case REGMAP_LITTLE_ENDIAN: + out_le32(addr, *val); + break; + case REGMAP_BIG_ENDIAN: + out_be32(addr, *val); + break; + } +} + +#if defined(out_le64) && defined(out_be64) && defined(writeq) +static inline void __write_64(u64 *addr, const u64 *val, + enum regmap_endianness_t endianness) +{ + switch (endianness) { + case REGMAP_NATIVE_ENDIAN: + writeq(*val, addr); + break; + case REGMAP_LITTLE_ENDIAN: + out_le64(addr, *val); + break; + case REGMAP_BIG_ENDIAN: + out_be64(addr, *val); + break; + } +} +#endif + +int regmap_raw_write_range(struct regmap *map, uint range_num, uint offset, + const void *val, size_t val_len) +{ + struct regmap_range *range; + void *ptr; + + if (range_num >= map->range_count) { + debug("%s: range index %d larger than range count\n", + __func__, range_num); + return -ERANGE; + } + range = &map->ranges[range_num]; + + offset <<= map->reg_offset_shift; + if (offset + val_len > range->size) { + debug("%s: offset/size combination invalid\n", __func__); + return -ERANGE; + } + + ptr = map_physmem(range->start + offset, val_len, MAP_NOCACHE); + + switch (val_len) { + case REGMAP_SIZE_8: + __write_8(ptr, val, map->endianness); + break; + case REGMAP_SIZE_16: + __write_16(ptr, val, map->endianness); + break; + case REGMAP_SIZE_32: + __write_32(ptr, val, map->endianness); + break; +#if defined(out_le64) && defined(out_be64) && defined(writeq) + case REGMAP_SIZE_64: + __write_64(ptr, val, map->endianness); + break; +#endif + default: + debug("%s: regmap size %zu unknown\n", __func__, val_len); + return -EINVAL; + } + + return 0; +} + +int regmap_raw_write(struct regmap *map, uint offset, const void *val, + size_t val_len) +{ + return regmap_raw_write_range(map, 0, offset, val, val_len); +} + +int regmap_write(struct regmap *map, uint offset, uint val) +{ + union { + u8 v8; + u16 v16; + u32 v32; + u64 v64; + } u; + + switch (map->width) { + case REGMAP_SIZE_8: + u.v8 = val; + break; + case REGMAP_SIZE_16: + u.v16 = val; + break; + case REGMAP_SIZE_32: + u.v32 = val; + break; + case REGMAP_SIZE_64: + u.v64 = val; + break; + default: + debug("%s: regmap size %zu unknown\n", __func__, + (size_t)map->width); + return -EINVAL; + } + + return regmap_raw_write(map, offset, &u, map->width); +} + +int regmap_update_bits(struct regmap *map, uint offset, uint mask, uint val) +{ + uint reg; + int ret; + + ret = regmap_read(map, offset, ®); + if (ret) + return ret; + + reg &= ~mask; + + return regmap_write(map, offset, reg | (val & mask)); +} + +int regmap_field_read(struct regmap_field *field, unsigned int *val) +{ + int ret; + unsigned int reg_val; + + ret = regmap_read(field->regmap, field->reg, ®_val); + if (ret != 0) + return ret; + + reg_val &= field->mask; + reg_val >>= field->shift; + *val = reg_val; + + return ret; +} + +int regmap_field_write(struct regmap_field *field, unsigned int val) +{ + return regmap_update_bits(field->regmap, field->reg, field->mask, + val << field->shift); +} + +static void regmap_field_init(struct regmap_field *rm_field, + struct regmap *regmap, + struct reg_field reg_field) +{ + rm_field->regmap = regmap; + rm_field->reg = reg_field.reg; + rm_field->shift = reg_field.lsb; + rm_field->mask = GENMASK(reg_field.msb, reg_field.lsb); +} + +struct regmap_field *devm_regmap_field_alloc(struct udevice *dev, + struct regmap *regmap, + struct reg_field reg_field) +{ + struct regmap_field *rm_field = devm_kzalloc(dev, sizeof(*rm_field), + GFP_KERNEL); + if (!rm_field) + return ERR_PTR(-ENOMEM); + + regmap_field_init(rm_field, regmap, reg_field); + + return rm_field; +} + +void devm_regmap_field_free(struct udevice *dev, struct regmap_field *field) +{ + devm_kfree(dev, field); +} + +struct regmap_field *regmap_field_alloc(struct regmap *regmap, + struct reg_field reg_field) +{ + struct regmap_field *rm_field = kzalloc(sizeof(*rm_field), GFP_KERNEL); + + if (!rm_field) + return ERR_PTR(-ENOMEM); + + regmap_field_init(rm_field, regmap, reg_field); + + return rm_field; +} + +void regmap_field_free(struct regmap_field *field) +{ + kfree(field); +} diff --git a/roms/u-boot/drivers/core/root.c b/roms/u-boot/drivers/core/root.c new file mode 100644 index 000000000..fe0562cd6 --- /dev/null +++ b/roms/u-boot/drivers/core/root.c @@ -0,0 +1,433 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + */ + +#include <common.h> +#include <errno.h> +#include <fdtdec.h> +#include <log.h> +#include <malloc.h> +#include <asm-generic/sections.h> +#include <asm/global_data.h> +#include <linux/libfdt.h> +#include <dm/acpi.h> +#include <dm/device.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/of.h> +#include <dm/of_access.h> +#include <dm/platdata.h> +#include <dm/read.h> +#include <dm/root.h> +#include <dm/uclass.h> +#include <dm/util.h> +#include <linux/list.h> + +DECLARE_GLOBAL_DATA_PTR; + +static 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; +} + +void dm_fixup_for_gd_move(struct global_data *new_gd) +{ + /* The sentinel node has moved, so update things that point to it */ + if (gd->dm_root) { + new_gd->uclass_root->next->prev = new_gd->uclass_root; + new_gd->uclass_root->prev->next = new_gd->uclass_root; + } +} + +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 *) + ((ulong)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->of_to_plat) + entry->of_to_plat += 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_driver); + const int n_ents = ll_entry_count(struct uclass_driver, uclass_driver); + 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->plat) + entry->plat += gd->reloc_off; + } +} + +static int dm_setup_inst(void) +{ + DM_ROOT_NON_CONST = DM_DEVICE_GET(root); + + if (CONFIG_IS_ENABLED(OF_PLATDATA_RT)) { + struct udevice_rt *urt; + void *base; + int n_ents; + uint size; + + /* Allocate the udevice_rt table */ + n_ents = ll_entry_count(struct udevice, udevice); + urt = calloc(n_ents, sizeof(struct udevice_rt)); + if (!urt) + return log_msg_ret("urt", -ENOMEM); + gd_set_dm_udevice_rt(urt); + + /* Now allocate space for the priv/plat data, and copy it in */ + size = __priv_data_end - __priv_data_start; + + base = calloc(1, size); + if (!base) + return log_msg_ret("priv", -ENOMEM); + memcpy(base, __priv_data_start, size); + gd_set_dm_priv_base(base); + } + + return 0; +} + +int dm_init(bool of_live) +{ + int ret; + + if (gd->dm_root) { + dm_warn("Virtual root driver already exists!\n"); + return -EINVAL; + } + if (CONFIG_IS_ENABLED(OF_PLATDATA_INST)) { + gd->uclass_root = &uclass_head; + } else { + gd->uclass_root = &DM_UCLASS_ROOT_S_NON_CONST; + INIT_LIST_HEAD(DM_UCLASS_ROOT_NON_CONST); + } + + if (IS_ENABLED(CONFIG_NEEDS_MANUAL_RELOC)) { + fix_drivers(); + fix_uclass(); + fix_devices(); + } + + if (CONFIG_IS_ENABLED(OF_PLATDATA_INST)) { + ret = dm_setup_inst(); + if (ret) { + log_debug("dm_setup_inst() failed: %d\n", ret); + return ret; + } + } else { + ret = device_bind_by_name(NULL, false, &root_info, + &DM_ROOT_NON_CONST); + if (ret) + return ret; + if (CONFIG_IS_ENABLED(OF_CONTROL)) + dev_set_ofnode(DM_ROOT_NON_CONST, ofnode_root()); + ret = device_probe(DM_ROOT_NON_CONST); + if (ret) + return ret; + } + + return 0; +} + +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; + + return 0; +} + +#if CONFIG_IS_ENABLED(DM_DEVICE_REMOVE) +int dm_remove_devices_flags(uint flags) +{ + device_remove(dm_root(), flags); + + return 0; +} +#endif + +int dm_scan_plat(bool pre_reloc_only) +{ + int ret; + + if (CONFIG_IS_ENABLED(OF_PLATDATA_DRIVER_RT)) { + struct driver_rt *dyn; + int n_ents; + + n_ents = ll_entry_count(struct driver_info, driver_info); + dyn = calloc(n_ents, sizeof(struct driver_rt)); + if (!dyn) + return -ENOMEM; + gd_set_dm_driver_rt(dyn); + } + + 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) && !CONFIG_IS_ENABLED(OF_PLATDATA) +/** + * dm_scan_fdt_node() - Scan the device tree and bind drivers for a node + * + * This scans the subnodes of a device tree node and and creates a driver + * for each one. + * + * @parent: Parent device for the devices that will be created + * @node: Node to scan + * @pre_reloc_only: If true, bind only drivers with the DM_FLAG_PRE_RELOC + * flag. If false bind all drivers. + * @return 0 if OK, -ve on error + */ +static int dm_scan_fdt_node(struct udevice *parent, ofnode parent_node, + bool pre_reloc_only) +{ + int ret = 0, err = 0; + ofnode node; + + if (!ofnode_valid(parent_node)) + return 0; + + for (node = ofnode_first_subnode(parent_node); + ofnode_valid(node); + node = ofnode_next_subnode(node)) { + const char *node_name = ofnode_get_name(node); + + if (!ofnode_is_enabled(node)) { + pr_debug(" - ignoring disabled device\n"); + continue; + } + err = lists_bind_fdt(parent, node, NULL, pre_reloc_only); + if (err && !ret) { + ret = err; + debug("%s: ret=%d\n", node_name, ret); + } + } + + if (ret) + dm_warn("Some drivers failed to bind\n"); + + return ret; +} + +int dm_scan_fdt_dev(struct udevice *dev) +{ + return dm_scan_fdt_node(dev, dev_ofnode(dev), + gd->flags & GD_FLG_RELOC ? false : true); +} + +int dm_scan_fdt(bool pre_reloc_only) +{ + return dm_scan_fdt_node(gd->dm_root, ofnode_root(), pre_reloc_only); +} + +static int dm_scan_fdt_ofnode_path(const char *path, bool pre_reloc_only) +{ + ofnode node; + + node = ofnode_path(path); + + return dm_scan_fdt_node(gd->dm_root, node, pre_reloc_only); +} + +int dm_extended_scan(bool pre_reloc_only) +{ + int ret, i; + const char * const nodes[] = { + "/chosen", + "/clocks", + "/firmware" + }; + + ret = dm_scan_fdt(pre_reloc_only); + if (ret) { + debug("dm_scan_fdt() failed: %d\n", ret); + return ret; + } + + /* Some nodes aren't devices themselves but may contain some */ + for (i = 0; i < ARRAY_SIZE(nodes); i++) { + ret = dm_scan_fdt_ofnode_path(nodes[i], pre_reloc_only); + if (ret) { + debug("dm_scan_fdt() scan for %s failed: %d\n", + nodes[i], ret); + return ret; + } + } + + return ret; +} +#endif + +__weak int dm_scan_other(bool pre_reloc_only) +{ + return 0; +} + +#if CONFIG_IS_ENABLED(OF_PLATDATA_INST) && CONFIG_IS_ENABLED(READ_ONLY) +void *dm_priv_to_rw(void *priv) +{ + long offset = priv - (void *)__priv_data_start; + + return gd_dm_priv_base() + offset; +} +#endif + +/** + * dm_scan() - Scan tables to bind devices + * + * Runs through the driver_info tables and binds the devices it finds. Then runs + * through the devicetree nodes. Finally calls dm_scan_other() to add any + * special devices + * + * @pre_reloc_only: If true, bind only nodes with special devicetree properties, + * or drivers with the DM_FLAG_PRE_RELOC flag. If false bind all drivers. + */ +static int dm_scan(bool pre_reloc_only) +{ + int ret; + + ret = dm_scan_plat(pre_reloc_only); + if (ret) { + debug("dm_scan_plat() failed: %d\n", ret); + return ret; + } + + if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) { + ret = dm_extended_scan(pre_reloc_only); + if (ret) { + debug("dm_extended_scan() failed: %d\n", ret); + return ret; + } + } + + ret = dm_scan_other(pre_reloc_only); + if (ret) + return ret; + + return 0; +} + +int dm_init_and_scan(bool pre_reloc_only) +{ + int ret; + + ret = dm_init(CONFIG_IS_ENABLED(OF_LIVE)); + if (ret) { + debug("dm_init() failed: %d\n", ret); + return ret; + } + if (!CONFIG_IS_ENABLED(OF_PLATDATA_INST)) { + ret = dm_scan(pre_reloc_only); + if (ret) { + log_debug("dm_scan() failed: %d\n", ret); + return ret; + } + } + + return 0; +} + +#ifdef CONFIG_ACPIGEN +static int root_acpi_get_name(const struct udevice *dev, char *out_name) +{ + return acpi_copy_name(out_name, "\\_SB"); +} + +struct acpi_ops root_acpi_ops = { + .get_name = root_acpi_get_name, +}; +#endif + +/* This is the root driver - all drivers are children of this */ +U_BOOT_DRIVER(root_driver) = { + .name = "root_driver", + .id = UCLASS_ROOT, + ACPI_OPS_PTR(&root_acpi_ops) +}; + +/* This is the root uclass */ +UCLASS_DRIVER(root) = { + .name = "root", + .id = UCLASS_ROOT, +}; diff --git a/roms/u-boot/drivers/core/simple-bus.c b/roms/u-boot/drivers/core/simple-bus.c new file mode 100644 index 000000000..18f52d26d --- /dev/null +++ b/roms/u-boot/drivers/core/simple-bus.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2014 Google, Inc + */ + +#include <common.h> +#include <asm/global_data.h> +#include <dm.h> +#include <dm/simple_bus.h> +#include <fdt_support.h> + +DECLARE_GLOBAL_DATA_PTR; + +fdt_addr_t simple_bus_translate(struct udevice *dev, fdt_addr_t addr) +{ + struct simple_bus_plat *plat = dev_get_uclass_plat(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) +{ +#if CONFIG_IS_ENABLED(OF_PLATDATA) + return 0; +#else + struct simple_bus_plat *plat = dev_get_uclass_plat(dev); + int ret; + + if (CONFIG_IS_ENABLED(SIMPLE_BUS_CORRECT_RANGE)) { + uint64_t caddr, paddr, len; + + /* only read range index 0 */ + ret = fdt_read_range((void *)gd->fdt_blob, dev_of_offset(dev), + 0, &caddr, &paddr, &len); + if (!ret) { + plat->base = caddr; + plat->target = paddr; + plat->size = len; + } + } else { + u32 cell[3]; + + ret = dev_read_u32_array(dev, "ranges", cell, + ARRAY_SIZE(cell)); + if (!ret) { + plat->base = cell[0]; + plat->target = cell[1]; + plat->size = cell[2]; + } + } + + return dm_scan_fdt_dev(dev); +#endif +} + +UCLASS_DRIVER(simple_bus) = { + .id = UCLASS_SIMPLE_BUS, + .name = "simple_bus", + .post_bind = simple_bus_post_bind, + .per_device_plat_auto = sizeof(struct simple_bus_plat), +}; + +#if !CONFIG_IS_ENABLED(OF_PLATDATA) +static const struct udevice_id generic_simple_bus_ids[] = { + { .compatible = "simple-bus" }, + { .compatible = "simple-mfd" }, + { } +}; +#endif + +U_BOOT_DRIVER(simple_bus) = { + .name = "simple_bus", + .id = UCLASS_SIMPLE_BUS, + .of_match = of_match_ptr(generic_simple_bus_ids), + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/roms/u-boot/drivers/core/simple-pm-bus.c b/roms/u-boot/drivers/core/simple-pm-bus.c new file mode 100644 index 000000000..1bb0d86e2 --- /dev/null +++ b/roms/u-boot/drivers/core/simple-pm-bus.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com> + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> + +/* + * Power domains are taken care of by driver_probe, so we just have to enable + * clocks + */ +static int simple_pm_bus_probe(struct udevice *dev) +{ + int ret; + struct clk_bulk *bulk = dev_get_priv(dev); + + ret = clk_get_bulk(dev, bulk); + if (ret) + return ret; + + ret = clk_enable_bulk(bulk); + if (ret && ret != -ENOSYS) { + clk_release_bulk(bulk); + return ret; + } + return 0; +} + +static int simple_pm_bus_remove(struct udevice *dev) +{ + int ret; + struct clk_bulk *bulk = dev_get_priv(dev); + + ret = clk_release_bulk(bulk); + if (ret && ret != -ENOSYS) + return ret; + else + return 0; +} + +static const struct udevice_id simple_pm_bus_ids[] = { + { .compatible = "simple-pm-bus" }, + { } +}; + +U_BOOT_DRIVER(simple_pm_bus_drv) = { + .name = "simple_pm_bus", + .id = UCLASS_SIMPLE_BUS, + .of_match = simple_pm_bus_ids, + .probe = simple_pm_bus_probe, + .remove = simple_pm_bus_remove, + .priv_auto = sizeof(struct clk_bulk), + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/roms/u-boot/drivers/core/syscon-uclass.c b/roms/u-boot/drivers/core/syscon-uclass.c new file mode 100644 index 000000000..cb33facc7 --- /dev/null +++ b/roms/u-boot/drivers/core/syscon-uclass.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#define LOG_CATEGORY UCLASS_SYSCON + +#include <common.h> +#include <log.h> +#include <syscon.h> +#include <dm.h> +#include <errno.h> +#include <regmap.h> +#include <dm/device-internal.h> +#include <dm/device_compat.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <linux/err.h> + +/* + * Caution: + * This API requires the given device has already been bound to the syscon + * driver. For example, + * + * compatible = "syscon", "simple-mfd"; + * + * works, but + * + * compatible = "simple-mfd", "syscon"; + * + * does not. The behavior is different from Linux. + */ +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); + + /* Special case for PCI devices, which don't have a regmap */ + if (device_get_uclass_id(dev->parent) == UCLASS_PCI) + return 0; + + /* + * With OF_PLATDATA we really have no way of knowing the format of + * the device-specific platform data. So we assume that it starts with + * a 'reg' member, and this holds a single address and size. Drivers + * using OF_PLATDATA will need to ensure that this is true. + */ +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct syscon_base_plat *plat = dev_get_plat(dev); + + return regmap_init_mem_plat(dev, plat->reg, ARRAY_SIZE(plat->reg), + &priv->regmap); +#else + return regmap_init_mem(dev_ofnode(dev), &priv->regmap); +#endif +} + +static int syscon_probe_by_ofnode(ofnode node, struct udevice **devp) +{ + struct udevice *dev, *parent; + int ret; + + /* found node with "syscon" compatible, not bounded to SYSCON UCLASS */ + if (!ofnode_device_is_compatible(node, "syscon")) { + log_debug("invalid compatible for syscon device\n"); + return -EINVAL; + } + + /* bound to driver with same ofnode or to root if not found */ + if (device_find_global_by_ofnode(node, &parent)) + parent = dm_root(); + + /* force bound to syscon class */ + ret = device_bind_driver_to_node(parent, "syscon", + ofnode_get_name(node), + node, &dev); + if (ret) { + dev_dbg(dev, "unable to bound syscon device\n"); + return ret; + } + ret = device_probe(dev); + if (ret) { + dev_dbg(dev, "unable to probe syscon device\n"); + return ret; + } + + *devp = dev; + return 0; +} + +struct regmap *syscon_regmap_lookup_by_phandle(struct udevice *dev, + const char *name) +{ + struct udevice *syscon; + struct regmap *r; + u32 phandle; + ofnode node; + int err; + + err = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, + name, &syscon); + if (err) { + /* found node with "syscon" compatible, not bounded to SYSCON */ + err = ofnode_read_u32(dev_ofnode(dev), name, &phandle); + if (err) + return ERR_PTR(err); + + node = ofnode_get_by_phandle(phandle); + if (!ofnode_valid(node)) { + dev_dbg(dev, "unable to find syscon device\n"); + return ERR_PTR(-EINVAL); + } + err = syscon_probe_by_ofnode(node, &syscon); + if (err) + return ERR_PTR(-ENODEV); + } + + r = syscon_get_regmap(syscon); + if (!r) { + dev_dbg(dev, "unable to find regmap\n"); + return ERR_PTR(-ENODEV); + } + + return r; +} + +int syscon_get_by_driver_data(ulong driver_data, struct udevice **devp) +{ + int ret; + + *devp = NULL; + + ret = uclass_first_device_drvdata(UCLASS_SYSCON, driver_data, devp); + if (ret) + return ret; + + return 0; +} + +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 = sizeof(struct syscon_uc_info), + .pre_probe = syscon_pre_probe, +}; + +static const struct udevice_id generic_syscon_ids[] = { + { .compatible = "syscon" }, + { } +}; + +U_BOOT_DRIVER(generic_syscon) = { + .name = "syscon", + .id = UCLASS_SYSCON, +#if !CONFIG_IS_ENABLED(OF_PLATDATA) + .bind = dm_scan_fdt_dev, +#endif + .of_match = generic_syscon_ids, +}; + +/* + * Linux-compatible syscon-to-regmap + * The syscon node can be bound to another driver, but still works + * as a syscon provider. + */ +struct regmap *syscon_node_to_regmap(ofnode node) +{ + struct udevice *dev; + struct regmap *r; + + if (uclass_get_device_by_ofnode(UCLASS_SYSCON, node, &dev)) + if (syscon_probe_by_ofnode(node, &dev)) + return ERR_PTR(-ENODEV); + + r = syscon_get_regmap(dev); + if (!r) { + dev_dbg(dev, "unable to find regmap\n"); + return ERR_PTR(-ENODEV); + } + + return r; +} diff --git a/roms/u-boot/drivers/core/uclass.c b/roms/u-boot/drivers/core/uclass.c new file mode 100644 index 000000000..117d35ac4 --- /dev/null +++ b/roms/u-boot/drivers/core/uclass.c @@ -0,0 +1,786 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + */ + +#define LOG_CATEGORY LOGC_DM + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <log.h> +#include <malloc.h> +#include <asm/global_data.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) { + void *ptr; + + ptr = calloc(1, uc_drv->priv_auto); + if (!ptr) { + ret = -ENOMEM; + goto fail_mem; + } + uclass_set_priv(uc, ptr); + } + 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) { + free(uclass_get_priv(uc)); + uclass_set_priv(uc, 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, DM_REMOVE_NORMAL | DM_REMOVE_NO_PD); + if (ret) + return log_msg_ret("remove", ret); + ret = device_unbind(dev); + if (ret) + return log_msg_ret("unbind", ret); + } + + uc_drv = uc->uc_drv; + if (uc_drv->destroy) + uc_drv->destroy(uc); + list_del(&uc->sibling_node); + if (uc_drv->priv_auto) + free(uclass_get_priv(uc)); + 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) { + if (CONFIG_IS_ENABLED(OF_PLATDATA_INST)) + return -ENOENT; + return uclass_add(id, ucp); + } + *ucp = uc; + + return 0; +} + +const char *uclass_get_name(enum uclass_id id) +{ + struct uclass *uc; + + if (uclass_get(id, &uc)) + return NULL; + return uc->uc_drv->name; +} + +void *uclass_get_priv(const struct uclass *uc) +{ + return uc->priv_; +} + +void uclass_set_priv(struct uclass *uc, void *priv) +{ + uc->priv_ = priv; +} + +enum uclass_id uclass_get_by_name(const char *name) +{ + int i; + + for (i = 0; i < UCLASS_COUNT; i++) { + struct uclass_driver *uc_drv = lists_uclass_lookup(i); + + if (uc_drv && !strcmp(uc_drv->name, name)) + return i; + } + + return UCLASS_INVALID; +} + +int dev_get_uclass_index(struct udevice *dev, struct uclass **ucp) +{ + struct udevice *iter; + struct uclass *uc = dev->uclass; + int i = 0; + + if (list_empty(&uc->dev_head)) + return -ENODEV; + + uclass_foreach_dev(iter, uc) { + if (iter == dev) { + if (ucp) + *ucp = uc; + return i; + } + i++; + } + + return -ENODEV; +} + +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; + + uclass_foreach_dev(dev, uc) { + 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; + + uclass_foreach_dev(dev, uc) { + if (!strcmp(dev->name, name)) { + *devp = dev; + return 0; + } + } + + return -ENODEV; +} + +int uclass_find_next_free_seq(struct uclass *uc) +{ + struct udevice *dev; + int max = -1; + + /* If using aliases, start with the highest alias value */ + if (CONFIG_IS_ENABLED(DM_SEQ_ALIAS) && + (uc->uc_drv->flags & DM_UC_FLAG_SEQ_ALIAS)) + max = dev_read_alias_highest_id(uc->uc_drv->name); + + /* Avoid conflict with existing devices */ + list_for_each_entry(dev, &uc->dev_head, uclass_node) { + if (dev->seq_ > max) + max = dev->seq_; + } + /* + * At this point, max will be -1 if there are no existing aliases or + * devices + */ + + return max + 1; +} + +int uclass_find_device_by_seq(enum uclass_id id, int seq, struct udevice **devp) +{ + struct uclass *uc; + struct udevice *dev; + int ret; + + *devp = NULL; + log_debug("%d\n", seq); + if (seq == -1) + return -ENODEV; + ret = uclass_get(id, &uc); + if (ret) + return ret; + + uclass_foreach_dev(dev, uc) { + log_debug(" - %d '%s'\n", dev->seq_, dev->name); + if (dev->seq_ == seq) { + *devp = dev; + log_debug(" - found\n"); + return 0; + } + } + log_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; + + uclass_foreach_dev(dev, uc) { + if (dev_of_offset(dev) == node) { + *devp = dev; + return 0; + } + } + + return -ENODEV; +} + +int uclass_find_device_by_ofnode(enum uclass_id id, ofnode node, + struct udevice **devp) +{ + struct uclass *uc; + struct udevice *dev; + int ret; + + log(LOGC_DM, LOGL_DEBUG, "Looking for %s\n", ofnode_get_name(node)); + *devp = NULL; + if (!ofnode_valid(node)) + return -ENODEV; + ret = uclass_get(id, &uc); + if (ret) + return ret; + + uclass_foreach_dev(dev, uc) { + log(LOGC_DM, LOGL_DEBUG_CONTENT, " - checking %s\n", + dev->name); + if (ofnode_equal(dev_ofnode(dev), node)) { + *devp = dev; + goto done; + } + } + ret = -ENODEV; + +done: + log(LOGC_DM, LOGL_DEBUG, " - result for %s: %s (ret=%d)\n", + ofnode_get_name(node), *devp ? (*devp)->name : "(none)", ret); + return ret; +} + +#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) +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 = dev_read_u32_default(parent, name, -1); + if (find_phandle <= 0) + return -ENOENT; + ret = uclass_get(id, &uc); + if (ret) + return ret; + + uclass_foreach_dev(dev, uc) { + uint phandle; + + phandle = dev_read_phandle(dev); + + if (phandle == find_phandle) { + *devp = dev; + return 0; + } + } + + return -ENODEV; +} +#endif + +int uclass_get_device_by_driver(enum uclass_id id, + const struct driver *find_drv, + struct udevice **devp) +{ + struct udevice *dev; + struct uclass *uc; + int ret; + + ret = uclass_get(id, &uc); + if (ret) + return ret; + + uclass_foreach_dev(dev, uc) { + if (dev->driver == find_drv) + return uclass_get_device_tail(dev, 0, devp); + } + + return -ENODEV; +} + +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, &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); +} + +int uclass_get_device_by_ofnode(enum uclass_id id, ofnode node, + struct udevice **devp) +{ + struct udevice *dev; + int ret; + + log(LOGC_DM, LOGL_DEBUG, "Looking for %s\n", ofnode_get_name(node)); + *devp = NULL; + ret = uclass_find_device_by_ofnode(id, node, &dev); + log(LOGC_DM, LOGL_DEBUG, " - result for %s: %s (ret=%d)\n", + ofnode_get_name(node), dev ? dev->name : "(none)", ret); + + return uclass_get_device_tail(dev, ret, devp); +} + +#if CONFIG_IS_ENABLED(OF_CONTROL) +int uclass_get_device_by_phandle_id(enum uclass_id id, uint phandle_id, + struct udevice **devp) +{ + struct udevice *dev; + struct uclass *uc; + int ret; + + *devp = NULL; + ret = uclass_get(id, &uc); + if (ret) + return ret; + + uclass_foreach_dev(dev, uc) { + uint phandle; + + phandle = dev_read_phandle(dev); + + if (phandle == phandle_id) { + *devp = dev; + return uclass_get_device_tail(dev, ret, devp); + } + } + + return -ENODEV; +} + +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_next_device_err(struct udevice **devp) +{ + int ret; + + ret = uclass_next_device(devp); + if (ret) + return ret; + else if (!*devp) + return -ENODEV; + + return 0; +} + +int uclass_first_device_check(enum uclass_id id, struct udevice **devp) +{ + int ret; + + *devp = NULL; + ret = uclass_find_first_device(id, devp); + if (ret) + return ret; + if (!*devp) + return 0; + + return device_probe(*devp); +} + +int uclass_next_device_check(struct udevice **devp) +{ + int ret; + + ret = uclass_find_next_device(devp); + if (ret) + return ret; + if (!*devp) + return 0; + + return device_probe(*devp); +} + +int uclass_first_device_drvdata(enum uclass_id id, ulong driver_data, + struct udevice **devp) +{ + struct udevice *dev; + struct uclass *uc; + + uclass_id_foreach_dev(id, dev, uc) { + if (dev_get_driver_data(dev) == driver_data) { + *devp = dev; + + return device_probe(dev); + } + } + + return -ENODEV; +} + +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_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) { + ret = uc_drv->child_pre_probe(dev); + if (ret) + return ret; + } + + return 0; +} + +int uclass_post_probe_device(struct udevice *dev) +{ + struct uclass_driver *uc_drv; + int ret; + + if (dev->parent) { + uc_drv = dev->parent->uclass->uc_drv; + if (uc_drv->child_post_probe) { + ret = uc_drv->child_post_probe(dev); + if (ret) + return ret; + } + } + + uc_drv = dev->uclass->uc_drv; + if (uc_drv->post_probe) { + ret = uc_drv->post_probe(dev); + if (ret) + return ret; + } + + 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 + +int uclass_probe_all(enum uclass_id id) +{ + struct udevice *dev; + int ret; + + ret = uclass_first_device(id, &dev); + if (ret || !dev) + return ret; + + /* Scanning uclass to probe all devices */ + while (dev) { + ret = uclass_next_device(&dev); + if (ret) + return ret; + } + + return 0; +} + +UCLASS_DRIVER(nop) = { + .id = UCLASS_NOP, + .name = "nop", +}; diff --git a/roms/u-boot/drivers/core/util.c b/roms/u-boot/drivers/core/util.c new file mode 100644 index 000000000..91e93b0cf --- /dev/null +++ b/roms/u-boot/drivers/core/util.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013 Google, Inc + */ + +#include <common.h> +#include <dm/device.h> +#include <dm/ofnode.h> +#include <dm/read.h> +#include <dm/util.h> +#include <linux/libfdt.h> +#include <vsprintf.h> + +#if CONFIG_IS_ENABLED(DM_WARN) +void dm_warn(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +} +#endif + +int list_count_items(struct list_head *head) +{ + struct list_head *node; + int count = 0; + + list_for_each(node, head) + count++; + + return count; +} + +#if !CONFIG_IS_ENABLED(OF_PLATDATA) +int pci_get_devfn(struct udevice *dev) +{ + struct fdt_pci_addr addr; + int ret; + + /* Extract the devfn from fdt_pci_addr */ + ret = ofnode_read_pci_addr(dev_ofnode(dev), FDT_PCI_SPACE_CONFIG, + "reg", &addr); + if (ret) { + if (ret != -ENOENT) + return -EINVAL; + } + + return addr.phys_hi & 0xff00; +} +#endif |