summaryrefslogtreecommitdiffstats
path: root/drivers/of/of_firmware.c
blob: 0135631fb89964f51e4a51f6c8a18958228170f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2019 Pengutronix, Michael Tretter <m.tretter@pengutronix.de>
 */
#include <common.h>
#include <firmware.h>
#include <of.h>

struct overlay_info {
	const char *firmware_path;
};

static struct firmware_mgr *of_node_get_mgr(struct device_node *np)
{
	struct device_node *mgr_node;

	do {
		if (of_device_is_compatible(np, "fpga-region")) {
			mgr_node = of_parse_phandle(np, "fpga-mgr", 0);
			if (mgr_node)
				return firmwaremgr_find_by_node(mgr_node);
		}
	} while ((np = of_get_parent(np)) != NULL);

	return NULL;
}

static int load_firmware(struct device_node *target,
			 struct device_node *fragment, void *data)
{
	struct overlay_info *info = data;
	const char *firmware_name;
	const char *firmware_path = info->firmware_path;
	char *firmware;
	int err;
	struct firmware_mgr *mgr;

	err = of_property_read_string(fragment,
				      "firmware-name", &firmware_name);
	/* Nothing to do if property does not exist. */
	if (err == -EINVAL)
		return 0;
	else if (err)
		return -EINVAL;

	mgr = of_node_get_mgr(target);
	if (!mgr)
		return -EINVAL;

	firmware = basprintf("%s/%s", firmware_path, firmware_name);
	if (!firmware)
		return -ENOMEM;

	err = firmwaremgr_load_file(mgr, firmware);

	free(firmware);

	return err;
}

int of_firmware_load_overlay(struct device_node *overlay, const char *path)
{
	struct overlay_info info = {
		.firmware_path = path,
	};
	int err;
	struct device_node *root;
	struct device_node *resolved;
	struct device_node *ovl;

	root = of_get_root_node();
	/*
	 * If we cannot resolve the symbols in the overlay, ensure that the
	 * overlay does depend on firmware to be loaded.
	 */
	resolved = of_resolve_phandles(root, overlay);
	ovl = resolved ? resolved : overlay;

	err = of_process_overlay(root, ovl,
				 load_firmware, &info);

	if (resolved)
		of_delete_node(resolved);

	return err;
}