summaryrefslogtreecommitdiffstats
path: root/drivers/of/of_firmware.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/of/of_firmware.c')
-rw-r--r--drivers/of/of_firmware.c111
1 files changed, 87 insertions, 24 deletions
diff --git a/drivers/of/of_firmware.c b/drivers/of/of_firmware.c
index 12ce1d95d0..2d8245a02e 100644
--- a/drivers/of/of_firmware.c
+++ b/drivers/of/of_firmware.c
@@ -21,12 +21,21 @@ static struct firmware_mgr *of_node_get_mgr(struct device_node *np)
return NULL;
}
+struct fw_load_entry {
+ struct firmware_mgr *mgr;
+ char *firmware;
+ struct list_head list;
+};
+
+static LIST_HEAD(fw_load_list);
+
static int load_firmware(struct device_node *target,
struct device_node *fragment, void *unused)
{
const char *firmware_name;
int err;
struct firmware_mgr *mgr;
+ struct fw_load_entry *fle;
err = of_property_read_string(fragment,
"firmware-name", &firmware_name);
@@ -43,32 +52,86 @@ static int load_firmware(struct device_node *target,
if (!mgr)
return -EINVAL;
- err = firmwaremgr_load_file(mgr, firmware_name);
+ fle = xzalloc(sizeof(*fle));
+ fle->mgr = mgr;
+ fle->firmware = xstrdup(firmware_name);
- return err;
+ list_add_tail(&fle->list, &fw_load_list);
+
+ return 0;
}
-int of_firmware_load_overlay(struct device_node *overlay)
+/*
+ * The dt overlay API says that a "firmware-name" property found in an overlay
+ * node compatible to "fpga-region" triggers loading of the firmware with the
+ * name given in the "firmware-name" property.
+ *
+ * barebox applies overlays to the Kernel device tree as part of booting the
+ * Kernel. When a firmware is needed for an overlay then it shall be loaded,
+ * so that the Kernel already finds the firmware loaded.
+ *
+ * In barebox overlays are applied as a of_fixup to the desired tree. The fixups
+ * might be executed multiple times not only as part of booting the Kernel, but
+ * also during of_diff command execution and other actions. It's not desired
+ * that we (re-)load all firmwares each time this happens, so the process is
+ * splitted up. During application of an overlay the needed firmwares are only
+ * collected to a list, but not actually loaded. Only once it's clear we want to
+ * boot with that device tree the firmwares are loaded by explicitly calling
+ * of_overlay_load_firmware().
+ */
+
+/**
+ * of_overlay_pre_load_firmware() - check overlay node for firmware to load
+ * @root: The device tree to apply the overlay to
+ * @overlay: The overlay
+ *
+ * This function checks the given overlay for firmware to load. If a firmware
+ * is needed then it is not directly loaded, but instead added to a list of
+ * firmware to be loaded. The firmware files on this list can then be loaded
+ * with of_overlay_load_firmware().
+ *
+ * Return: 0 for success or negative error code otherwise
+ */
+int of_overlay_pre_load_firmware(struct device_node *root, struct device_node *overlay)
{
- int err;
- struct device_node *root;
- struct device_node *resolved;
- struct device_node *ovl;
-
- root = of_get_root_node();
- resolved = of_resolve_phandles(root, overlay);
- /*
- * If the overlay cannot be resolved, use the load_firmware callback
- * with the unresolved overlay to verify that the overlay does not
- * depend on a firmware to be loaded. If a required firmware cannot be
- * loaded, the overlay must not be applied.
- */
- ovl = resolved ? resolved : overlay;
-
- err = of_process_overlay(root, ovl, load_firmware, NULL);
-
- if (resolved)
- of_delete_node(resolved);
-
- return err;
+ return of_process_overlay(root, overlay, load_firmware, NULL);
+}
+
+/**
+ * of_overlay_load_firmware() - load all firmware files
+ *
+ * This function loads all firmware files previously collected in
+ * of_overlay_pre_load_firmware().
+ *
+ * Return: 0 when all firmware files could be loaded, negative error code
+ * otherwise.
+ */
+int of_overlay_load_firmware(void)
+{
+ struct fw_load_entry *fle;
+ int ret;
+
+ list_for_each_entry(fle, &fw_load_list, list) {
+ ret = firmwaremgr_load_file(fle->mgr, fle->firmware);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * of_overlay_load_firmware_clear() - Clear list of firmware files
+ *
+ * This function clears the list of firmware files.
+ */
+void of_overlay_load_firmware_clear(void)
+{
+ struct fw_load_entry *fle, *tmp;
+
+ list_for_each_entry_safe(fle, tmp, &fw_load_list, list) {
+ list_del(&fle->list);
+ free(fle->firmware);
+ free(fle);
+ }
}