summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2021-07-18 07:13:02 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2021-07-18 07:13:02 +0200
commit8a5144d02a0146c4f0ec577232c0a102d12c8ee7 (patch)
tree85b0459bb60bfaa2a765e21bb7e19a0edc6c2d6a
parent2551aad17a15fe06ae3b76b12b5df2207c1e11f6 (diff)
parent368deb2baa56a6ab0f4ec7d7a42559c4db14b063 (diff)
downloadbarebox-8a5144d02a0146c4f0ec577232c0a102d12c8ee7.tar.gz
barebox-8a5144d02a0146c4f0ec577232c0a102d12c8ee7.tar.xz
Merge branch 'for-next/firmware'
-rw-r--r--Documentation/user/devicetree.rst45
-rw-r--r--arch/arm/boards/qemu-virt/board.c4
-rw-r--r--arch/arm/boards/raspberry-pi/rpi-common.c2
-rw-r--r--arch/arm/boards/webasto-ccbv2/board.c2
-rw-r--r--arch/arm/lib32/bootm.c6
-rw-r--r--arch/arm/lib32/bootu.c5
-rw-r--r--arch/arm/lib32/bootz.c3
-rw-r--r--arch/arm/lib64/armlinux.c5
-rw-r--r--arch/arm/mach-socfpga/include/mach/cyclone5-regs.h1
-rw-r--r--arch/kvx/lib/bootm.c6
-rw-r--r--arch/mips/lib/bootm.c4
-rw-r--r--arch/powerpc/lib/ppclinux.c4
-rw-r--r--arch/riscv/lib/bootm.c5
-rw-r--r--commands/of_diff.c6
-rw-r--r--commands/of_display_timings.c2
-rw-r--r--commands/of_dump.c4
-rw-r--r--commands/of_overlay.c34
-rw-r--r--commands/of_property.c74
-rw-r--r--commands/oftree.c2
-rw-r--r--common/blspec.c99
-rw-r--r--common/bootm.c4
-rw-r--r--common/efi/efi.c2
-rw-r--r--common/firmware.c109
-rw-r--r--common/image-fit.c2
-rw-r--r--common/oftree.c4
-rw-r--r--common/state/backend_format_dtb.c2
-rw-r--r--drivers/Kconfig1
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/firmware/Makefile2
-rw-r--r--drivers/firmware/altera_serial.c1
-rw-r--r--drivers/firmware/socfpga.c97
-rw-r--r--drivers/firmware/socfpga_sdr.S20
-rw-r--r--drivers/firmware/zynqmp-fpga.c2
-rw-r--r--drivers/fpga/Kconfig30
-rw-r--r--drivers/fpga/Makefile7
-rw-r--r--drivers/fpga/fpga-bridge.c243
-rw-r--r--drivers/fpga/socfpga-fpga2sdram-bridge.c139
-rw-r--r--drivers/fpga/socfpga-hps2fpga-bridge.c179
-rw-r--r--drivers/of/Kconfig1
-rw-r--r--drivers/of/base.c8
-rw-r--r--drivers/of/fdt.c17
-rw-r--r--drivers/of/of_firmware.c137
-rw-r--r--drivers/of/overlay.c355
-rw-r--r--drivers/reset/core.c2
-rw-r--r--include/filetype.h14
-rw-r--r--include/firmware.h27
-rw-r--r--include/fpga-bridge.h74
-rw-r--r--include/fpga-mgr.h102
-rw-r--r--include/libbb.h4
-rw-r--r--include/libfile.h1
-rw-r--r--include/linux/reset.h2
-rw-r--r--include/of.h43
-rw-r--r--lib/libbb.c58
-rw-r--r--lib/libfile.c23
54 files changed, 1760 insertions, 266 deletions
diff --git a/Documentation/user/devicetree.rst b/Documentation/user/devicetree.rst
index 679cae7f00..91afffdcda 100644
--- a/Documentation/user/devicetree.rst
+++ b/Documentation/user/devicetree.rst
@@ -21,7 +21,7 @@ The internal devicetree
-----------------------
The devicetree consulted by barebox plays a special role. It is referred to
-as the "internal devicetree." The barebox devicetree commands work on this
+as the "internal devicetree" or "live tree". The barebox devicetree commands work on this
devicetree. The devicetree source (DTS) files are kept in sync with the kernel DTS
files. As the FDT files are meant to be backward compatible, it should always be possible
to start a kernel with the barebox internal devicetree. However, since the barebox
@@ -75,3 +75,46 @@ It is important to know that these commands normally work on the internal
devicetree. If you want to modify the devicetree the kernel is started with
see the -f options to of_property and of_node. This option will register the
operation for later execution on the Kernel devicetree.
+
+Device tree overlays
+--------------------
+
+barebox has support for device tree overlays. barebox knows two different trees,
+the live tree and the device tree the kernel is started with. Both can be applied
+overlays to.
+
+Device tree overlays on the live tree
+.....................................
+
+While the live tree can be patched by board code, barebox does not
+detect any changes to the live tree. To let the overlays have any effect, board
+code must make sure the live tree is patched before the devices are instanciated
+from it.
+
+Device tree overlays on the kernel device tree
+..............................................
+
+Overlays can be applied to the kernel device tree before it is handed over to
+the kernel. The behaviour is controlled by different variables:
+
+``global.of.overlay.dir``
+ Overlays are read from this directory. barebox will try to apply all overlays
+ found here if not limited by one of the other variables below. When the path
+ given here is an absolute path it is used as is. A relative path is relative
+ to ``/`` or relative to the rootfs when using bootloader spec.
+``global.of.overlay.compatible``
+ This is a space separated list of compatibles. Only overlays matching one of
+ these compatibles will be applied. When this list is empty then all overlays
+ will be applied. Overlays that don't have a compatible are considered being
+ always compatible.
+``global.of.overlay.filepattern``
+ This is a space separated list of file patterns. An overlay is only applied
+ when its filename matches one of the patterns. The patterns can contain
+ ``*`` and ``?`` as wildcards. The default is ``*`` which means all files are
+ applied.
+``global.of.overlay.filter``
+ This is a space separated list of filters to apply. There are two generic filters:
+ ``filepattern`` matches ``global.of.overlay.filepattern`` above, ``compatible`` matches
+ ``global.of.overlay.compatible`` above. The default is ``filepattern compatible``
+ which means the two generic filters are active. This list may be replaced or
+ supplemented by board specific filters.
diff --git a/arch/arm/boards/qemu-virt/board.c b/arch/arm/boards/qemu-virt/board.c
index 5ce1ecfc24..b2a3cb29ab 100644
--- a/arch/arm/boards/qemu-virt/board.c
+++ b/arch/arm/boards/qemu-virt/board.c
@@ -31,14 +31,14 @@ static int replace_dtb(void) {
return 0;
}
- root = of_unflatten_dtb(fdt);
+ root = of_unflatten_dtb(fdt, INT_MAX);
if (!of_device_is_compatible(root, "linux,dummy-virt")) {
of_delete_node(root);
return 0;
}
- overlay = of_unflatten_dtb(__dtb_overlay_of_flash_start);
+ overlay = of_unflatten_dtb(__dtb_overlay_of_flash_start, INT_MAX);
of_overlay_apply_tree(root, overlay);
return barebox_register_of(root);
diff --git a/arch/arm/boards/raspberry-pi/rpi-common.c b/arch/arm/boards/raspberry-pi/rpi-common.c
index e326732b3a..6c5df6fd69 100644
--- a/arch/arm/boards/raspberry-pi/rpi-common.c
+++ b/arch/arm/boards/raspberry-pi/rpi-common.c
@@ -430,7 +430,7 @@ static int rpi_vc_fdt_bootargs(void *fdt)
struct device_node *root = NULL, *node;
const char *cmdline;
- root = of_unflatten_dtb(fdt);
+ root = of_unflatten_dtb(fdt, INT_MAX);
if (IS_ERR(root)) {
ret = PTR_ERR(root);
root = NULL;
diff --git a/arch/arm/boards/webasto-ccbv2/board.c b/arch/arm/boards/webasto-ccbv2/board.c
index a78258ea6a..477771309e 100644
--- a/arch/arm/boards/webasto-ccbv2/board.c
+++ b/arch/arm/boards/webasto-ccbv2/board.c
@@ -28,7 +28,7 @@ static int ccbv2_probe(struct device_d *dev)
return 0;
fdt = (void*)OPTEE_OVERLAY_LOCATION;
- overlay = of_unflatten_dtb(fdt);
+ overlay = of_unflatten_dtb(fdt, INT_MAX);
if (IS_ERR(overlay))
return PTR_ERR(overlay);
diff --git a/arch/arm/lib32/bootm.c b/arch/arm/lib32/bootm.c
index 28a645a9d0..2bba585e96 100644
--- a/arch/arm/lib32/bootm.c
+++ b/arch/arm/lib32/bootm.c
@@ -325,6 +325,10 @@ static int __do_bootm_linux(struct image_data *data, unsigned long free_mem,
if (data->dryrun)
return 0;
+ ret = of_overlay_load_firmware();
+ if (ret)
+ return ret;
+
if (data->tee_res)
tee = (void *)data->tee_res->start;
else
@@ -421,7 +425,7 @@ static int do_bootz_linux_fdt(int fd, struct image_data *data, void **outfdt)
if (IS_BUILTIN(CONFIG_OFTREE)) {
struct device_node *root;
- root = of_unflatten_dtb(oftree);
+ root = of_unflatten_dtb(oftree, header->totalsize);
if (IS_ERR(root)) {
pr_err("unable to unflatten devicetree\n");
goto err_free;
diff --git a/arch/arm/lib32/bootu.c b/arch/arm/lib32/bootu.c
index 24c744da58..0540a16280 100644
--- a/arch/arm/lib32/bootu.c
+++ b/arch/arm/lib32/bootu.c
@@ -8,7 +8,7 @@
static int do_bootu(int argc, char *argv[])
{
- int fd;
+ int fd, ret;
void *kernel = NULL;
void *oftree = NULL;
@@ -25,6 +25,9 @@ static int do_bootu(int argc, char *argv[])
#ifdef CONFIG_OFTREE
oftree = of_get_fixed_tree(NULL);
#endif
+ ret = of_overlay_load_firmware();
+ if (ret)
+ return ret;
start_linux(kernel, 0, 0, 0, oftree, ARM_STATE_SECURE, NULL);
diff --git a/arch/arm/lib32/bootz.c b/arch/arm/lib32/bootz.c
index a2a26ac2f9..486e945a74 100644
--- a/arch/arm/lib32/bootz.c
+++ b/arch/arm/lib32/bootz.c
@@ -111,6 +111,9 @@ static int do_bootz(int argc, char *argv[])
#ifdef CONFIG_OFTREE
oftree = of_get_fixed_tree(NULL);
#endif
+ ret = of_overlay_load_firmware();
+ if (ret)
+ return ret;
start_linux(zimage, swap, 0, 0, oftree, ARM_STATE_SECURE, NULL);
diff --git a/arch/arm/lib64/armlinux.c b/arch/arm/lib64/armlinux.c
index e05f29984d..8382ffdf1b 100644
--- a/arch/arm/lib64/armlinux.c
+++ b/arch/arm/lib64/armlinux.c
@@ -11,6 +11,7 @@ static int do_bootm_linux(struct image_data *data)
void (*fn)(unsigned long dtb, unsigned long x1, unsigned long x2,
unsigned long x3);
phys_addr_t devicetree;
+ int ret;
fn = booti_load_image(data, &devicetree);
if (IS_ERR(fn))
@@ -19,6 +20,10 @@ static int do_bootm_linux(struct image_data *data)
if (data->dryrun)
return 0;
+ ret = of_overlay_load_firmware();
+ if (ret)
+ return ret;
+
shutdown_barebox();
fn(devicetree, 0, 0, 0);
diff --git a/arch/arm/mach-socfpga/include/mach/cyclone5-regs.h b/arch/arm/mach-socfpga/include/mach/cyclone5-regs.h
index e88daf7189..1a7d787a27 100644
--- a/arch/arm/mach-socfpga/include/mach/cyclone5-regs.h
+++ b/arch/arm/mach-socfpga/include/mach/cyclone5-regs.h
@@ -18,5 +18,6 @@
#define CYCLONE5_SYSMGR_ADDRESS 0xffd08000
#define CYCLONE5_SCANMGR_ADDRESS 0xfff02000
#define CYCLONE5_SMP_TWD_ADDRESS 0xfffec600
+#define CYCLONE5_OCRAM_ADDRESS 0xffff0000
#endif /* __MACH_SOCFPGA_REGS_H */
diff --git a/arch/kvx/lib/bootm.c b/arch/kvx/lib/bootm.c
index 198eef7980..4d17e1846d 100644
--- a/arch/kvx/lib/bootm.c
+++ b/arch/kvx/lib/bootm.c
@@ -24,11 +24,17 @@ typedef void __noreturn (*boot_func_entry)(unsigned long, void *);
static int do_boot_entry(struct image_data *data, boot_func_entry entry,
void *fdt_load_addr)
{
+ int ret;
+
printf("starting elf (entry at %p)\n", entry);
if (data->dryrun)
return 0;
+ ret = of_overlay_load_firmware();
+ if (ret)
+ return ret;
+
shutdown_barebox();
/* Synchronize I-cache with D-cache */
diff --git a/arch/mips/lib/bootm.c b/arch/mips/lib/bootm.c
index 6c56202ea9..b71b8d5c38 100644
--- a/arch/mips/lib/bootm.c
+++ b/arch/mips/lib/bootm.c
@@ -65,6 +65,10 @@ static int do_bootm_elf(struct image_data *data)
if (data->dryrun)
goto bootm_free_fdt;
+ ret = of_overlay_load_firmware();
+ if (ret)
+ return ret;
+
shutdown_barebox();
entry = (void *) (unsigned long) data->os_address;
diff --git a/arch/powerpc/lib/ppclinux.c b/arch/powerpc/lib/ppclinux.c
index 05c29be7da..b4cb59a524 100644
--- a/arch/powerpc/lib/ppclinux.c
+++ b/arch/powerpc/lib/ppclinux.c
@@ -67,6 +67,10 @@ static int do_bootm_linux(struct image_data *data)
if (data->dryrun)
return 0;
+ ret = of_overlay_load_firmware();
+ if (ret)
+ return ret;
+
/* Relocate the device tree if outside the initial
* Linux mapped TLB.
*/
diff --git a/arch/riscv/lib/bootm.c b/arch/riscv/lib/bootm.c
index b3e41de4a8..8504ee63a9 100644
--- a/arch/riscv/lib/bootm.c
+++ b/arch/riscv/lib/bootm.c
@@ -8,11 +8,16 @@ static int do_bootm_linux(struct image_data *data)
{
void (*fn)(unsigned long a0, unsigned long dtb, unsigned long a2);
phys_addr_t devicetree;
+ int ret;
fn = booti_load_image(data, &devicetree);
if (IS_ERR(fn))
return PTR_ERR(fn);
+ ret = of_overlay_load_firmware();
+ if (ret)
+ return ret;
+
shutdown_barebox();
fn(0, devicetree, 0);
diff --git a/commands/of_diff.c b/commands/of_diff.c
index ec039776cf..fa99fcd641 100644
--- a/commands/of_diff.c
+++ b/commands/of_diff.c
@@ -25,7 +25,7 @@ static struct device_node *get_tree(const char *filename, struct device_node *ro
if (!node)
return ERR_PTR(-ENOENT);
- return of_copy_node(NULL, node);
+ return of_dup(node);
}
if (!strcmp(filename, "+")) {
@@ -33,7 +33,7 @@ static struct device_node *get_tree(const char *filename, struct device_node *ro
if (!node)
return ERR_PTR(-ENOENT);
- node = of_copy_node(NULL, root);
+ node = of_dup(root);
of_fix_tree(node);
@@ -44,7 +44,7 @@ static struct device_node *get_tree(const char *filename, struct device_node *ro
if (ret)
return ERR_PTR(ret);
- node = of_unflatten_dtb(fdt);
+ node = of_unflatten_dtb(fdt, size);
free(fdt);
diff --git a/commands/of_display_timings.c b/commands/of_display_timings.c
index 27b91f311a..4e5ec223b7 100644
--- a/commands/of_display_timings.c
+++ b/commands/of_display_timings.c
@@ -83,7 +83,7 @@ static int do_of_display_timings(int argc, char *argv[])
return -EINVAL;
}
- root = of_unflatten_dtb(fdt);
+ root = of_unflatten_dtb(fdt, size);
free(fdt);
diff --git a/commands/of_dump.c b/commands/of_dump.c
index 2089c07ef7..5223ba63ad 100644
--- a/commands/of_dump.c
+++ b/commands/of_dump.c
@@ -71,7 +71,7 @@ static int do_of_dump(int argc, char *argv[])
return -errno;
}
- root = of_unflatten_dtb(fdt);
+ root = of_unflatten_dtb(fdt, size);
free(fdt);
@@ -88,7 +88,7 @@ static int do_of_dump(int argc, char *argv[])
/* create a copy of internal devicetree */
void *fdt;
fdt = of_flatten_dtb(root);
- root = of_unflatten_dtb(fdt);
+ root = of_unflatten_dtb(fdt, fdt_totalsize(fdt));
free(fdt);
diff --git a/commands/of_overlay.c b/commands/of_overlay.c
index aa4b6484ca..b3660b4bf1 100644
--- a/commands/of_overlay.c
+++ b/commands/of_overlay.c
@@ -13,41 +13,25 @@
static int do_of_overlay(int argc, char *argv[])
{
- int opt, ret;
+ int ret;
struct fdt_header *fdt;
struct device_node *overlay;
- const char *search_path = NULL;
+ size_t size;
- while ((opt = getopt(argc, argv, "S:")) > 0) {
- switch (opt) {
- case 'S':
- search_path = optarg;
- break;
- default:
- return COMMAND_ERROR_USAGE;
- }
- }
-
- if (argc != optind + 1)
+ if (argc != 2)
return COMMAND_ERROR_USAGE;
- fdt = read_file(argv[optind], NULL);
+ fdt = read_file(argv[optind], &size);
if (!fdt) {
printf("cannot read %s\n", argv[optind]);
return 1;
}
- overlay = of_unflatten_dtb(fdt);
+ overlay = of_unflatten_dtb(fdt, size);
free(fdt);
if (IS_ERR(overlay))
return PTR_ERR(overlay);
- if (search_path) {
- ret = of_firmware_load_overlay(overlay, search_path);
- if (ret)
- goto err;
- }
-
ret = of_register_overlay(overlay);
if (ret) {
printf("cannot apply oftree overlay: %s\n", strerror(-ret));
@@ -61,15 +45,9 @@ err:
return ret;
}
-BAREBOX_CMD_HELP_START(of_overlay)
-BAREBOX_CMD_HELP_TEXT("Options:")
-BAREBOX_CMD_HELP_OPT("-S path", "load firmware using this search path")
-BAREBOX_CMD_HELP_END
-
BAREBOX_CMD_START(of_overlay)
.cmd = do_of_overlay,
BAREBOX_CMD_DESC("register device tree overlay as fixup")
- BAREBOX_CMD_OPTS("[-S path] FILE")
+ BAREBOX_CMD_OPTS("FILE")
BAREBOX_CMD_GROUP(CMD_GRP_MISC)
- BAREBOX_CMD_HELP(cmd_of_overlay_help)
BAREBOX_CMD_END
diff --git a/commands/of_property.c b/commands/of_property.c
index 3d5097165b..063e97775c 100644
--- a/commands/of_property.c
+++ b/commands/of_property.c
@@ -4,6 +4,7 @@
/* of_property.c - device tree property handling support */
#include <common.h>
+#include <libfile.h>
#include <environment.h>
#include <fdt.h>
#include <of.h>
@@ -18,7 +19,7 @@
#include <init.h>
#include <xfuncs.h>
-static int of_parse_prop_cells(char * const *newval, int count, char *data, int *len)
+static int of_parse_prop_cells(struct device_node *root, char * const *newval, int count, char *data, int *len)
{
char *cp;
unsigned long tmp; /* holds converted values */
@@ -60,7 +61,7 @@ static int of_parse_prop_cells(char * const *newval, int count, char *data, int
str = xzalloc(len + 1);
strncpy(str, cp, len);
- n = of_find_node_by_path_or_alias(NULL, str);
+ n = of_find_node_by_path_or_alias(root, str);
if (!n)
printf("Cannot find node '%s'\n", str);
@@ -164,7 +165,7 @@ static int of_parse_prop_string(char * const *newval, int count, char *data, int
* data: A bytestream to be placed in the property
* len: The length of the resulting bytestream
*/
-static int of_parse_prop(char * const *newval, int count, char *data, int *len)
+static int of_parse_prop(struct device_node *root, char * const *newval, int count, char *data, int *len)
{
char *newp; /* temporary newval char pointer */
@@ -177,7 +178,7 @@ static int of_parse_prop(char * const *newval, int count, char *data, int *len)
switch (*newp) {
case '<':
- return of_parse_prop_cells(newval, count, data, len);
+ return of_parse_prop_cells(root, newval, count, data, len);
case '[':
return of_parse_prop_stream(newval, count, data, len);
default:
@@ -302,8 +303,13 @@ static int do_of_property(int argc, char *argv[])
int set = 0;
int fixup = 0;
char *path, *propname;
+ char *dtbfile = NULL;
+ int ret = 0;
+ size_t size;
+ struct fdt_header *fdt = NULL;
+ struct device_node *root = NULL;
- while ((opt = getopt(argc, argv, "dsf")) > 0) {
+ while ((opt = getopt(argc, argv, "dsfe:")) > 0) {
switch (opt) {
case 'd':
delete = 1;
@@ -314,6 +320,9 @@ static int do_of_property(int argc, char *argv[])
case 'f':
fixup = 1;
break;
+ case 'e':
+ dtbfile = optarg;
+ break;
default:
return COMMAND_ERROR_USAGE;
}
@@ -327,8 +336,22 @@ static int do_of_property(int argc, char *argv[])
debug("path: %s propname: %s\n", path, propname);
+ if ( !(set || delete))
+ return COMMAND_ERROR_USAGE;
+
+ if (dtbfile) {
+ fdt = read_file(dtbfile, &size);
+ if (!fdt) {
+ printf("unable to read %s: %m\n", dtbfile);
+ return -errno;
+ }
+
+ root = of_unflatten_dtb(fdt, size);
+
+ free(fdt);
+ }
+
if (set) {
- int ret;
int len;
void *data;
@@ -340,10 +363,10 @@ static int do_of_property(int argc, char *argv[])
if (!data)
return -ENOMEM;
- ret = of_parse_prop(&argv[optind + 2], argc - optind - 2, data, &len);
+ ret = of_parse_prop(root, &argv[optind + 2], argc - optind - 2, data, &len);
if (ret) {
free(data);
- return ret;
+ goto out;
}
if (fixup) {
@@ -351,21 +374,37 @@ static int do_of_property(int argc, char *argv[])
if (ret)
free(data);
} else {
- ret = do_of_property_set_now(NULL, path, propname, data, len);
+ ret = do_of_property_set_now(root, path, propname, data, len);
free(data);
}
- return ret;
- }
+ if (ret)
+ goto out;
- if (delete) {
+ } else if (delete) {
if (fixup)
- return do_of_property_delete_fixup(path, propname);
+ ret = do_of_property_delete_fixup(path, propname);
else
- return do_of_property_delete_now(NULL, path, propname);
+ ret = do_of_property_delete_now(root, path, propname);
+
+ if (ret)
+ goto out;
+ }
+
+ if (root && !fixup) {
+ fdt = of_flatten_dtb(root);
+
+ ret = write_file(dtbfile, fdt, fdt32_to_cpu(fdt->totalsize));
+
+ free(fdt);
}
- return COMMAND_ERROR_USAGE;
+out:
+
+ if (root)
+ of_delete_node(root);
+
+ return ret;
}
BAREBOX_CMD_HELP_START(of_property)
@@ -373,6 +412,7 @@ BAREBOX_CMD_HELP_TEXT("Options:")
BAREBOX_CMD_HELP_OPT ("-s", "set property to value")
BAREBOX_CMD_HELP_OPT ("-d", "delete property")
BAREBOX_CMD_HELP_OPT ("-f", "set/delete as a fixup (defer the action until booting)")
+BAREBOX_CMD_HELP_OPT ("-e dtb", "set/delete/fixup from external dtb")
BAREBOX_CMD_HELP_TEXT("")
BAREBOX_CMD_HELP_TEXT("Valid formats for values:")
BAREBOX_CMD_HELP_TEXT("<0x00112233 4 05> - an array of cells. cells not beginning with a digit are")
@@ -384,8 +424,8 @@ BAREBOX_CMD_HELP_END
BAREBOX_CMD_START(of_property)
.cmd = do_of_property,
BAREBOX_CMD_DESC("handle device tree properties")
- BAREBOX_CMD_OPTS("[-sd] [-f] NODE [PROPERTY] [VALUES]")
+ BAREBOX_CMD_OPTS("[-sd] [-e] [-f] NODE [PROPERTY] [VALUES]")
BAREBOX_CMD_GROUP(CMD_GRP_MISC)
- BAREBOX_CMD_COMPLETE(devicetree_complete)
+ BAREBOX_CMD_COMPLETE(devicetree_file_complete)
BAREBOX_CMD_HELP(cmd_of_property_help)
BAREBOX_CMD_END
diff --git a/commands/oftree.c b/commands/oftree.c
index 1d902f2830..7d4b08c9d3 100644
--- a/commands/oftree.c
+++ b/commands/oftree.c
@@ -82,7 +82,7 @@ static int do_oftree(int argc, char *argv[])
return 1;
}
- root = of_unflatten_dtb(fdt);
+ root = of_unflatten_dtb(fdt, size);
free(fdt);
diff --git a/common/blspec.c b/common/blspec.c
index ad80d7a8cd..6cb1fea9e8 100644
--- a/common/blspec.c
+++ b/common/blspec.c
@@ -32,79 +32,33 @@ int blspec_entry_var_set(struct blspec_entry *entry, const char *name,
val ? strlen(val) + 1 : 0, 1);
}
-static int blspec_apply_oftree_overlay(char *file, const char *abspath,
- int dryrun)
-{
- int ret = 0;
- struct fdt_header *fdt;
- struct device_node *overlay;
- char *path;
- char *firmware_path;
-
- path = basprintf("%s/%s", abspath, file);
-
- fdt = read_file(path, NULL);
- if (!fdt) {
- pr_warn("unable to read \"%s\"\n", path);
- ret = -EINVAL;
- goto out;
- }
-
- overlay = of_unflatten_dtb(fdt);
- free(fdt);
- if (IS_ERR(overlay)) {
- ret = PTR_ERR(overlay);
- goto out;
- }
-
- if (dryrun) {
- pr_info("dry run: skip overlay %s\n", path);
- of_delete_node(overlay);
- goto out;
- }
-
- /*
- * Unfortunately the device tree overlay contains only the filename of
- * the firmware and relies on the firmware search paths to find the
- * actual file. Use /lib/firmware in the Linux root directory and hope
- * for the best.
- */
- firmware_path = basprintf("%s/%s", abspath, "/lib/firmware");
- ret = of_firmware_load_overlay(overlay, firmware_path);
- free(firmware_path);
- if (ret) {
- pr_warn("failed to load firmware: skip overlay \"%s\"\n", path);
- of_delete_node(overlay);
- goto out;
- }
-
- ret = of_register_overlay(overlay);
- if (ret) {
- pr_warn("cannot register devicetree overlay \"%s\"\n", path);
- of_delete_node(overlay);
- }
-
-out:
- free(path);
-
- return ret;
-}
-
-static void blspec_apply_oftree_overlays(const char *overlays,
- const char *abspath, int dryrun)
+static int blspec_overlay_fixup(struct device_node *root, void *ctx)
{
+ struct blspec_entry *entry = ctx;
+ const char *overlays;
char *overlay;
char *sep, *freep;
+ overlays = blspec_entry_var_get(entry, "devicetree-overlay");
+
sep = freep = xstrdup(overlays);
while ((overlay = strsep(&sep, " "))) {
+ char *path;
+
if (!*overlay)
continue;
- blspec_apply_oftree_overlay(overlay, abspath, dryrun);
+
+ path = basprintf("%s/%s", entry->rootpath, overlay);
+
+ of_overlay_apply_file(root, path, false);
+
+ free(path);
}
free(freep);
+
+ return 0;
}
/*
@@ -121,6 +75,8 @@ static int blspec_boot(struct bootentry *be, int verbose, int dryrun)
const char *abspath, *devicetree, *options, *initrd, *linuximage;
const char *overlays;
const char *appendroot;
+ const char *old_fws;
+ char *fws;
struct bootm_data data = {
.dryrun = dryrun,
};
@@ -159,7 +115,7 @@ static int blspec_boot(struct bootentry *be, int verbose, int dryrun)
}
if (overlays)
- blspec_apply_oftree_overlays(overlays, abspath, dryrun);
+ of_register_fixup(blspec_overlay_fixup, entry);
if (initrd)
data.initrd_file = basprintf("%s/%s", abspath, initrd);
@@ -183,9 +139,26 @@ static int blspec_boot(struct bootentry *be, int verbose, int dryrun)
(entry->cdev && entry->cdev->dev) ?
dev_name(entry->cdev->dev) : "none");
+ of_overlay_set_basedir(abspath);
+
+ old_fws = firmware_get_searchpath();
+ if (old_fws && *old_fws)
+ fws = basprintf("%s/lib/firmware:%s", abspath, old_fws);
+ else
+ fws = basprintf("%s/lib/firmware", abspath);
+ firmware_set_searchpath(fws);
+ free(fws);
+
ret = bootm_boot(&data);
if (ret)
pr_err("Booting failed\n");
+
+ if (overlays)
+ of_unregister_fixup(blspec_overlay_fixup, entry);
+
+ of_overlay_set_basedir("/");
+ firmware_set_searchpath(old_fws);
+
err_out:
free((char *)data.oftree_file);
free((char *)data.initrd_file);
@@ -490,7 +463,7 @@ static bool entry_is_of_compatible(struct blspec_entry *entry)
goto out;
}
- root = of_unflatten_dtb(fdt);
+ root = of_unflatten_dtb(fdt, size);
if (IS_ERR(root)) {
ret = false;
root = NULL;
diff --git a/common/bootm.c b/common/bootm.c
index 644443a021..89e3e93f2c 100644
--- a/common/bootm.c
+++ b/common/bootm.c
@@ -361,7 +361,7 @@ void *bootm_get_devicetree(struct image_data *data)
if (ret)
return ERR_PTR(ret);
- data->of_root_node = of_unflatten_dtb(of_tree);
+ data->of_root_node = of_unflatten_dtb(of_tree, of_size);
} else if (data->oftree_file) {
size_t size;
@@ -389,7 +389,7 @@ void *bootm_get_devicetree(struct image_data *data)
if (ret)
return ERR_PTR(ret);
- data->of_root_node = of_unflatten_dtb(oftree);
+ data->of_root_node = of_unflatten_dtb(oftree, size);
free(oftree);
diff --git a/common/efi/efi.c b/common/efi/efi.c
index 01003dc00f..7f12342cf9 100644
--- a/common/efi/efi.c
+++ b/common/efi/efi.c
@@ -437,7 +437,7 @@ static int efi_late_init(void)
return -EINVAL;
}
- root = of_unflatten_dtb(fdt);
+ root = of_unflatten_dtb(fdt, size);
free(fdt);
diff --git a/common/firmware.c b/common/firmware.c
index 58509d5da6..b33acff77f 100644
--- a/common/firmware.c
+++ b/common/firmware.c
@@ -11,9 +11,13 @@
#include <libbb.h>
#include <libfile.h>
#include <fs.h>
+#include <globalvar.h>
+#include <magicvar.h>
#include <linux/list.h>
#include <linux/stat.h>
#include <linux/err.h>
+#include <uncompress.h>
+#include <filetype.h>
#define BUFSIZ 4096
@@ -61,13 +65,25 @@ struct firmware_mgr *firmwaremgr_find(const char *id)
* handler. This allows to retrieve the firmware handler with a phandle from
* the device tree.
*/
-struct firmware_mgr *firmwaremgr_find_by_node(const struct device_node *np)
+struct firmware_mgr *firmwaremgr_find_by_node(struct device_node *np)
{
struct firmware_mgr *mgr;
+ char *na, *nb;
- list_for_each_entry(mgr, &firmwaremgr_list, list)
- if (mgr->handler->dev->parent->device_node == np)
+ na = of_get_reproducible_name(np);
+
+ list_for_each_entry(mgr, &firmwaremgr_list, list) {
+ nb = of_get_reproducible_name(mgr->handler->device_node);
+ if (!strcmp(na, nb)) {
+ free(na);
+ free(nb);
return mgr;
+ }
+
+ free(nb);
+ }
+
+ free(na);
return NULL;
}
@@ -206,17 +222,96 @@ out:
return ret;
}
+static char *firmware_path;
+
+const char *firmware_get_searchpath(void)
+{
+ return firmware_path;
+}
+
+void firmware_set_searchpath(const char *path)
+{
+ free(firmware_path);
+ firmware_path = strdup(path);
+}
+
+static bool file_exists(const char *filename)
+{
+ struct stat s;
+
+ return !stat(filename, &s);
+}
+
/*
* firmware_load_file - load a firmware to a device
*/
int firmwaremgr_load_file(struct firmware_mgr *mgr, const char *firmware)
{
- int ret;
- char *name = basprintf("/dev/%s", mgr->handler->id);
+ char *dst;
+ enum filetype type;
+ int ret = 0;
+ char *fw = NULL;
+ int firmwarefd = 0;
+ int devicefd = 0;
+
+ if (!firmware)
+ return -EINVAL;
+
+ if (!mgr->handler->id) {
+ pr_err("id not defined for handler\n");
+ return -ENODEV;
+ }
+
+ dst = basprintf("/dev/%s", mgr->handler->id);
+
+ if (*firmware != '/') {
+ fw = find_path(firmware_path, firmware, file_exists);
+ if (fw)
+ firmware = fw;
+ }
+
+ firmwarefd = open(firmware, O_RDONLY);
+ if (firmwarefd < 0) {
+ printf("could not open %s: %s\n", firmware,
+ errno_str());
+ ret = firmwarefd;
+ goto out;
+ }
+
+ type = file_name_detect_type(firmware);
- ret = copy_file(firmware, name, 0);
+ devicefd = open(dst, O_WRONLY);
+ if (devicefd < 0) {
+ printf("could not open %s: %s\n", dst, errno_str());
+ ret = devicefd;
+ goto out;
+ }
- free(name);
+ if (file_is_compressed_file(type))
+ ret = uncompress_fd_to_fd(firmwarefd, devicefd,
+ uncompress_err_stdout);
+ else
+ ret = copy_fd(firmwarefd, devicefd);
+
+out:
+ free(dst);
+ free(fw);
+
+ if (firmwarefd > 0)
+ close(firmwarefd);
+ if (devicefd > 0)
+ close(devicefd);
return ret;
}
+
+static int firmware_init(void)
+{
+ firmware_path = strdup("/env/firmware");
+ globalvar_add_simple_string("firmware.path", &firmware_path);
+
+ return 0;
+}
+device_initcall(firmware_init);
+
+BAREBOX_MAGICVAR(global.firmware.path, "Firmware search path");
diff --git a/common/image-fit.c b/common/image-fit.c
index 2c5ef7f687..c1a34a4405 100644
--- a/common/image-fit.c
+++ b/common/image-fit.c
@@ -754,7 +754,7 @@ static int fit_do_open(struct fit_handle *handle)
const char *desc = "(no description)";
struct device_node *root;
- root = of_unflatten_dtb_const(handle->fit);
+ root = of_unflatten_dtb_const(handle->fit, handle->size);
if (IS_ERR(root))
return PTR_ERR(root);
diff --git a/common/oftree.c b/common/oftree.c
index 5eaa63ad7e..7028c49aca 100644
--- a/common/oftree.c
+++ b/common/oftree.c
@@ -326,6 +326,8 @@ int of_fix_tree(struct device_node *node)
struct of_fixup *of_fixup;
int ret;
+ of_overlay_load_firmware_clear();
+
list_for_each_entry(of_fixup, &of_fixup_list, list) {
ret = of_fixup->fixup(node, of_fixup->context);
if (ret)
@@ -353,7 +355,7 @@ struct fdt_header *of_get_fixed_tree(struct device_node *node)
if (!node)
return NULL;
- freenp = node = of_copy_node(NULL, node);
+ freenp = node = of_dup(node);
if (!node)
return NULL;
}
diff --git a/common/state/backend_format_dtb.c b/common/state/backend_format_dtb.c
index 48f30db1f5..d0fc948859 100644
--- a/common/state/backend_format_dtb.c
+++ b/common/state/backend_format_dtb.c
@@ -59,7 +59,7 @@ static int state_backend_format_dtb_verify(struct state_backend_format *format,
fdtb->root = NULL;
}
- root = of_unflatten_dtb(buf);
+ root = of_unflatten_dtb(buf, dtb_len);
if (IS_ERR(root)) {
dev_err(fdtb->dev, "Failed to unflatten dtb from buffer with length %zd, %ld\n",
len, PTR_ERR(root));
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 787d366933..670e0a9f4c 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -37,6 +37,7 @@ source "drivers/reset/Kconfig"
source "drivers/pci/Kconfig"
source "drivers/rtc/Kconfig"
source "drivers/firmware/Kconfig"
+source "drivers/fpga/Kconfig"
source "drivers/phy/Kconfig"
source "drivers/crypto/Kconfig"
source "drivers/memory/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index be5b0b3b04..e21ac7095c 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_RESET_CONTROLLER) += reset/
obj-$(CONFIG_PCI) += pci/
obj-y += rtc/
obj-$(CONFIG_FIRMWARE) += firmware/
+obj-$(CONFIG_FPGA) += fpga/
obj-$(CONFIG_GENERIC_PHY) += phy/
obj-$(CONFIG_HAB) += hab/
obj-$(CONFIG_CRYPTO_HW) += crypto/
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index b162b08b0a..bbd2bcda97 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -1,3 +1,3 @@
obj-$(CONFIG_FIRMWARE_ALTERA_SERIAL) += altera_serial.o
-obj-$(CONFIG_FIRMWARE_ALTERA_SOCFPGA) += socfpga.o
+obj-$(CONFIG_FIRMWARE_ALTERA_SOCFPGA) += socfpga.o socfpga_sdr.o
obj-$(CONFIG_FIRMWARE_ZYNQMP_FPGA) += zynqmp-fpga.o
diff --git a/drivers/firmware/altera_serial.c b/drivers/firmware/altera_serial.c
index 3a0175dd07..49460c6a70 100644
--- a/drivers/firmware/altera_serial.c
+++ b/drivers/firmware/altera_serial.c
@@ -371,6 +371,7 @@ static int altera_spi_probe(struct device_d *dev)
if (model)
fh->model = xstrdup(model);
fh->dev = dev;
+ fh->device_node = dev->device_node;
this->spi = (struct spi_device *)dev->type_data;
this->data = data;
diff --git a/drivers/firmware/socfpga.c b/drivers/firmware/socfpga.c
index 234fb2d094..dd9202bf2b 100644
--- a/drivers/firmware/socfpga.c
+++ b/drivers/firmware/socfpga.c
@@ -27,6 +27,7 @@
*/
#include <firmware.h>
+#include <fpga-mgr.h>
#include <command.h>
#include <common.h>
#include <malloc.h>
@@ -38,6 +39,9 @@
#include <mach/cyclone5-reset-manager.h>
#include <mach/cyclone5-regs.h>
#include <mach/cyclone5-sdram.h>
+#include <asm/fncpy.h>
+#include <mmu.h>
+#include <asm/cache.h>
#define FPGAMGRREGS_STAT 0x0
#define FPGAMGRREGS_CTRL 0x4
@@ -77,21 +81,17 @@
#define CDRATIO_x4 0x2
#define CDRATIO_x8 0x3
-struct fpgamgr {
- struct firmware_handler fh;
- struct device_d dev;
- void __iomem *regs;
- void __iomem *regs_data;
- int programmed;
-};
+extern void socfpga_sdram_apply_static_cfg(void __iomem *sdrctrlgrp);
+extern void socfpga_sdram_apply_static_cfg_end(void *);
+extern const u32 socfpga_sdram_apply_static_cfg_sz;
/* Get the FPGA mode */
-static uint32_t fpgamgr_get_mode(struct fpgamgr *mgr)
+static uint32_t socfpga_fpgamgr_get_mode(struct fpgamgr *mgr)
{
return readl(mgr->regs + FPGAMGRREGS_STAT) & FPGAMGRREGS_STAT_MODE_MASK;
}
-static int fpgamgr_dclkcnt_set(struct fpgamgr *mgr, unsigned long cnt)
+static int socfpga_fpgamgr_dclkcnt_set(struct fpgamgr *mgr, unsigned long cnt)
{
uint64_t start;
@@ -115,7 +115,7 @@ static int fpgamgr_dclkcnt_set(struct fpgamgr *mgr, unsigned long cnt)
}
/* Start the FPGA programming by initialize the FPGA Manager */
-static int fpgamgr_program_init(struct fpgamgr *mgr)
+static int socfpga_fpgamgr_program_init(struct fpgamgr *mgr)
{
unsigned long reg;
uint32_t ctrl = 0, ratio;
@@ -164,7 +164,7 @@ static int fpgamgr_program_init(struct fpgamgr *mgr)
/* (1) wait until FPGA enter reset phase */
start = get_time_ns();
while (1) {
- if (fpgamgr_get_mode(mgr) == FPGAMGRREGS_MODE_RESETPHASE)
+ if (socfpga_fpgamgr_get_mode(mgr) == FPGAMGRREGS_MODE_RESETPHASE)
break;
if (is_timeout(start, 100 * MSECOND))
return -ETIMEDOUT;
@@ -178,7 +178,7 @@ static int fpgamgr_program_init(struct fpgamgr *mgr)
/* (2) wait until FPGA enter configuration phase */
start = get_time_ns();
while (1) {
- if (fpgamgr_get_mode(mgr) == FPGAMGRREGS_MODE_CFGPHASE)
+ if (socfpga_fpgamgr_get_mode(mgr) == FPGAMGRREGS_MODE_CFGPHASE)
break;
if (is_timeout(start, 100 * MSECOND))
return -ETIMEDOUT;
@@ -196,7 +196,7 @@ static int fpgamgr_program_init(struct fpgamgr *mgr)
}
/* Ensure the FPGA entering config done */
-static int fpgamgr_program_poll_cd(struct fpgamgr *mgr)
+static int socfpga_fpgamgr_program_poll_cd(struct fpgamgr *mgr)
{
unsigned long reg;
uint32_t val;
@@ -230,18 +230,18 @@ static int fpgamgr_program_poll_cd(struct fpgamgr *mgr)
}
/* Ensure the FPGA entering init phase */
-static int fpgamgr_program_poll_initphase(struct fpgamgr *mgr)
+static int socfpga_fpgamgr_program_poll_initphase(struct fpgamgr *mgr)
{
uint64_t start;
/* additional clocks for the CB to enter initialization phase */
- if (fpgamgr_dclkcnt_set(mgr, 0x4) != 0)
+ if (socfpga_fpgamgr_dclkcnt_set(mgr, 0x4) != 0)
return -5;
/* (4) wait until FPGA enter init phase or user mode */
start = get_time_ns();
while (1) {
- int mode = fpgamgr_get_mode(mgr);
+ int mode = socfpga_fpgamgr_get_mode(mgr);
if (mode == FPGAMGRREGS_MODE_INITPHASE ||
mode == FPGAMGRREGS_MODE_USERMODE)
@@ -255,19 +255,19 @@ static int fpgamgr_program_poll_initphase(struct fpgamgr *mgr)
}
/* Ensure the FPGA entering user mode */
-static int fpgamgr_program_poll_usermode(struct fpgamgr *mgr)
+static int socfpga_fpgamgr_program_poll_usermode(struct fpgamgr *mgr)
{
uint32_t val;
uint64_t start;
/* additional clocks for the CB to exit initialization phase */
- if (fpgamgr_dclkcnt_set(mgr, 0x5000) != 0)
+ if (socfpga_fpgamgr_dclkcnt_set(mgr, 0x5000) != 0)
return -7;
/* (5) wait until FPGA enter user mode */
start = get_time_ns();
while (1) {
- if (fpgamgr_get_mode(mgr) == FPGAMGRREGS_MODE_USERMODE)
+ if (socfpga_fpgamgr_get_mode(mgr) == FPGAMGRREGS_MODE_USERMODE)
break;
if (is_timeout(start, 100 * MSECOND))
return -ETIMEDOUT;
@@ -285,7 +285,7 @@ static int fpgamgr_program_poll_usermode(struct fpgamgr *mgr)
* Using FPGA Manager to program the FPGA
* Return 0 for sucess
*/
-static int fpgamgr_program_start(struct firmware_handler *fh)
+static int socfpga_fpgamgr_program_start(struct firmware_handler *fh)
{
struct fpgamgr *mgr = container_of(fh, struct fpgamgr, fh);
int status;
@@ -295,19 +295,10 @@ static int fpgamgr_program_start(struct firmware_handler *fh)
/* disable all signals from hps peripheral controller to fpga */
writel(0, SYSMGR_FPGAINTF_MODULE);
- /* disable all signals from fpga to hps sdram */
- writel(0, (CYCLONE5_SDR_ADDRESS + SDR_CTRLGRP_FPGAPORTRST_ADDRESS));
-
- /* disable all axi bridge (hps2fpga, lwhps2fpga & fpga2hps) */
- writel(~0, CYCLONE5_RSTMGR_ADDRESS + RESET_MGR_BRG_MOD_RESET_OFS);
-
- /* unmap the bridges from NIC-301 */
- writel(0x1, CYCLONE5_L3REGS_ADDRESS);
-
dev_dbg(&mgr->dev, "start programming...\n");
/* initialize the FPGA Manager */
- status = fpgamgr_program_init(mgr);
+ status = socfpga_fpgamgr_program_init(mgr);
if (status) {
dev_err(&mgr->dev, "program init failed with: %s\n",
strerror(-status));
@@ -318,7 +309,7 @@ static int fpgamgr_program_start(struct firmware_handler *fh)
}
/* Write the RBF data to FPGA Manager */
-static int fpgamgr_program_write_buf(struct firmware_handler *fh, const void *buf,
+static int socfpga_fpgamgr_program_write_buf(struct firmware_handler *fh, const void *buf,
size_t size)
{
struct fpgamgr *mgr = container_of(fh, struct fpgamgr, fh);
@@ -349,13 +340,14 @@ static int fpgamgr_program_write_buf(struct firmware_handler *fh, const void *bu
return 0;
}
-static int fpgamgr_program_finish(struct firmware_handler *fh)
+static int socfpga_fpgamgr_program_finish(struct firmware_handler *fh)
{
struct fpgamgr *mgr = container_of(fh, struct fpgamgr, fh);
int status;
+ void (*ocram_func)(void __iomem *ocram_base);
/* Ensure the FPGA entering config done */
- status = fpgamgr_program_poll_cd(mgr);
+ status = socfpga_fpgamgr_program_poll_cd(mgr);
if (status) {
dev_err(&mgr->dev, "poll for config done failed with: %s\n",
strerror(-status));
@@ -365,7 +357,7 @@ static int fpgamgr_program_finish(struct firmware_handler *fh)
dev_dbg(&mgr->dev, "waiting for init phase...\n");
/* Ensure the FPGA entering init phase */
- status = fpgamgr_program_poll_initphase(mgr);
+ status = socfpga_fpgamgr_program_poll_initphase(mgr);
if (status) {
dev_err(&mgr->dev, "poll for init phase failed with: %s\n",
strerror(-status));
@@ -375,13 +367,26 @@ static int fpgamgr_program_finish(struct firmware_handler *fh)
dev_dbg(&mgr->dev, "waiting for user mode...\n");
/* Ensure the FPGA entering user mode */
- status = fpgamgr_program_poll_usermode(mgr);
+ status = socfpga_fpgamgr_program_poll_usermode(mgr);
if (status) {
dev_err(&mgr->dev, "poll for user mode with: %s\n",
strerror(-status));
return status;
}
+ remap_range((void *)CYCLONE5_OCRAM_ADDRESS, PAGE_SIZE, MAP_CACHED);
+
+ dev_dbg(&mgr->dev, "Setting APPLYCFG bit...\n");
+
+ ocram_func = fncpy((void __iomem *)CYCLONE5_OCRAM_ADDRESS,
+ &socfpga_sdram_apply_static_cfg,
+ socfpga_sdram_apply_static_cfg_sz);
+
+ sync_caches_for_execution();
+
+ ocram_func((void __iomem *) (CYCLONE5_SDR_ADDRESS +
+ SDR_CTRLGRP_STATICCFG_ADDRESS));
+
return 0;
}
@@ -389,11 +394,11 @@ static int fpgamgr_program_finish(struct firmware_handler *fh)
static int programmed_get(struct param_d *p, void *priv)
{
struct fpgamgr *mgr = priv;
- mgr->programmed = fpgamgr_get_mode(mgr) == FPGAMGRREGS_MODE_USERMODE;
+ mgr->programmed = socfpga_fpgamgr_get_mode(mgr) == FPGAMGRREGS_MODE_USERMODE;
return 0;
}
-static int fpgamgr_probe(struct device_d *dev)
+static int socfpga_fpgamgr_probe(struct device_d *dev)
{
struct resource *iores;
struct fpgamgr *mgr;
@@ -427,9 +432,9 @@ static int fpgamgr_probe(struct device_d *dev)
else
fh->id = xstrdup("socfpga-fpga");
- fh->open = fpgamgr_program_start;
- fh->write = fpgamgr_program_write_buf;
- fh->close = fpgamgr_program_finish;
+ fh->open = socfpga_fpgamgr_program_start;
+ fh->write = socfpga_fpgamgr_program_write_buf;
+ fh->close = socfpga_fpgamgr_program_finish;
of_property_read_string(dev->device_node, "compatible", &model);
if (model)
fh->model = xstrdup(model);
@@ -451,6 +456,8 @@ static int fpgamgr_probe(struct device_d *dev)
}
fh->dev = &mgr->dev;
+ fh->device_node = dev->device_node;
+
ret = firmwaremgr_register(fh);
if (ret != 0) {
free(mgr);
@@ -467,16 +474,16 @@ out:
return ret;
}
-static struct of_device_id fpgamgr_id_table[] = {
+static struct of_device_id socfpga_fpgamgr_id_table[] = {
{
.compatible = "altr,socfpga-fpga-mgr",
},
{ /* sentinel */ }
};
-static struct driver_d fpgamgr_driver = {
+static struct driver_d socfpga_fpgamgr_driver = {
.name = "socfpa-fpgamgr",
- .of_compatible = DRV_OF_COMPAT(fpgamgr_id_table),
- .probe = fpgamgr_probe,
+ .of_compatible = DRV_OF_COMPAT(socfpga_fpgamgr_id_table),
+ .probe = socfpga_fpgamgr_probe,
};
-device_platform_driver(fpgamgr_driver);
+device_platform_driver(socfpga_fpgamgr_driver);
diff --git a/drivers/firmware/socfpga_sdr.S b/drivers/firmware/socfpga_sdr.S
new file mode 100644
index 0000000000..b895fb293c
--- /dev/null
+++ b/drivers/firmware/socfpga_sdr.S
@@ -0,0 +1,20 @@
+#include <linux/linkage.h>
+
+ .arch armv7-a
+ .arm
+
+/*
+ * r0 : sdram controller staticcfg
+ */
+
+ENTRY(socfpga_sdram_apply_static_cfg)
+ push {ip,lr}
+ ldr r1, [r0]
+ orr r1, r1, #8
+ str r1, [r0]
+ pop {ip,pc}
+ .align
+ENDPROC(socfpga_sdram_apply_static_cfg)
+
+ENTRY(socfpga_sdram_apply_static_cfg_sz)
+ .word . - socfpga_sdram_apply_static_cfg;
diff --git a/drivers/firmware/zynqmp-fpga.c b/drivers/firmware/zynqmp-fpga.c
index ab70d99933..0fc229bfd3 100644
--- a/drivers/firmware/zynqmp-fpga.c
+++ b/drivers/firmware/zynqmp-fpga.c
@@ -383,6 +383,8 @@ static int zynqmp_fpga_probe(struct device_d *dev)
}
fh->dev = &mgr->dev;
+ fh->device_node = dev->device_node;
+
ret = firmwaremgr_register(fh);
if (ret != 0) {
free(mgr);
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
new file mode 100644
index 0000000000..64ce9f91b6
--- /dev/null
+++ b/drivers/fpga/Kconfig
@@ -0,0 +1,30 @@
+#
+# FPGA framework configuration
+#
+
+menu "FPGA Configuration Support"
+
+config FPGA
+ tristate "FPGA Configuration Framework"
+ help
+ Say Y here if you want support for configuring FPGAs from barebox.
+
+if FPGA
+
+config FPGA_BRIDGE
+ tristate "FPGA Bridge Framework"
+ help
+ Say Y here if you want to support bridges connected between host
+ processors and FPGAs or between FPGAs.
+
+config SOCFPGA_FPGA_BRIDGE
+ tristate "Altera SoCFPGA FPGA Bridges"
+ depends on ARCH_SOCFPGA && FPGA_BRIDGE
+ select RESET_CONTROLLER
+ help
+ Say Y to enable drivers for FPGA bridges for Altera SOCFPGA
+ devices.
+
+endif # FPGA
+
+endmenu
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
new file mode 100644
index 0000000000..86178fe7c0
--- /dev/null
+++ b/drivers/fpga/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the fpga framework and fpga manager drivers.
+#
+
+# FPGA Bridge Drivers
+obj-$(CONFIG_FPGA_BRIDGE) += fpga-bridge.o
+obj-$(CONFIG_SOCFPGA_FPGA_BRIDGE) += socfpga-hps2fpga-bridge.o socfpga-fpga2sdram-bridge.o
diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c
new file mode 100644
index 0000000000..6f9e943de9
--- /dev/null
+++ b/drivers/fpga/fpga-bridge.c
@@ -0,0 +1,243 @@
+/*
+ * FPGA Bridge Framework Driver
+ *
+ * Copyright (C) 2013-2016 Altera Corporation, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <common.h>
+#include <fpga-bridge.h>
+
+/**
+ * fpga_bridge_enable - Enable transactions on the bridge
+ *
+ * @bridge: FPGA bridge
+ *
+ * Return: 0 for success, error code otherwise.
+ */
+int fpga_bridge_enable(struct fpga_bridge *bridge)
+{
+ dev_dbg(&bridge->dev, "enable\n");
+
+ if (bridge->br_ops && bridge->br_ops->enable_set)
+ return bridge->br_ops->enable_set(bridge, 1);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fpga_bridge_enable);
+
+/**
+ * fpga_bridge_disable - Disable transactions on the bridge
+ *
+ * @bridge: FPGA bridge
+ *
+ * Return: 0 for success, error code otherwise.
+ */
+int fpga_bridge_disable(struct fpga_bridge *bridge)
+{
+ dev_dbg(&bridge->dev, "disable\n");
+
+ if (bridge->br_ops && bridge->br_ops->enable_set)
+ return bridge->br_ops->enable_set(bridge, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fpga_bridge_disable);
+
+/**
+ * of_fpga_bridge_get - get an exclusive reference to a fpga bridge
+ *
+ * @np: node pointer of a FPGA bridge
+ *
+ * Return fpga_bridge struct if successful.
+ * Return -EBUSY if someone already has a reference to the bridge.
+ * Return -ENODEV if @np is not a FPGA Bridge.
+ */
+struct fpga_bridge *of_fpga_bridge_get(struct device_node *np)
+
+{
+ struct device_d *dev;
+ struct fpga_bridge *bridge;
+ int ret = -EPROBE_DEFER;
+
+ dev = of_find_device_by_node(np);
+ if (!dev || !dev->priv)
+ return ERR_PTR(ret);
+
+ bridge = dev->priv;
+
+ return bridge;
+}
+EXPORT_SYMBOL_GPL(of_fpga_bridge_get);
+
+/**
+ * fpga_bridges_enable - enable bridges in a list
+ * @bridge_list: list of FPGA bridges
+ *
+ * Enable each bridge in the list. If list is empty, do nothing.
+ *
+ * Return 0 for success or empty bridge list; return error code otherwise.
+ */
+int fpga_bridges_enable(struct list_head *bridge_list)
+{
+ struct fpga_bridge *bridge;
+ int ret;
+
+ list_for_each_entry(bridge, bridge_list, node) {
+ ret = fpga_bridge_enable(bridge);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fpga_bridges_enable);
+
+/**
+ * fpga_bridges_disable - disable bridges in a list
+ *
+ * @bridge_list: list of FPGA bridges
+ *
+ * Disable each bridge in the list. If list is empty, do nothing.
+ *
+ * Return 0 for success or empty bridge list; return error code otherwise.
+ */
+int fpga_bridges_disable(struct list_head *bridge_list)
+{
+ struct fpga_bridge *bridge;
+ int ret;
+
+ list_for_each_entry(bridge, bridge_list, node) {
+ ret = fpga_bridge_disable(bridge);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fpga_bridges_disable);
+
+/**
+ * fpga_bridges_put - put bridges
+ *
+ * @bridge_list: list of FPGA bridges
+ *
+ * For each bridge in the list, put the bridge and remove it from the list.
+ * If list is empty, do nothing.
+ */
+void fpga_bridges_put(struct list_head *bridge_list)
+{
+ struct fpga_bridge *bridge, *next;
+
+ list_for_each_entry_safe(bridge, next, bridge_list, node)
+ list_del(&bridge->node);
+}
+EXPORT_SYMBOL_GPL(fpga_bridges_put);
+
+/**
+ * fpga_bridges_get_to_list - get a bridge, add it to a list
+ *
+ * @np: node pointer of a FPGA bridge
+ * @bridge_list: list of FPGA bridges
+ *
+ * Get an exclusive reference to the bridge and and it to the list.
+ *
+ * Return 0 for success, error code from of_fpga_bridge_get() othewise.
+ */
+int fpga_bridge_get_to_list(struct device_node *np,
+ struct list_head *bridge_list)
+{
+ struct fpga_bridge *bridge;
+
+ bridge = of_fpga_bridge_get(np);
+ if (IS_ERR(bridge))
+ return PTR_ERR(bridge);
+ list_add(&bridge->node, bridge_list);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fpga_bridge_get_to_list);
+
+static int set_enable(struct param_d *p, void *priv)
+{
+ struct fpga_bridge *bridge = priv;
+
+ if (bridge->enable)
+ fpga_bridge_enable(bridge);
+ else
+ fpga_bridge_disable(bridge);
+
+ return 0;
+}
+
+/**
+ * fpga_bridge_register - register a fpga bridge driver
+ * @dev: FPGA bridge device from pdev
+ * @name: FPGA bridge name
+ * @br_ops: pointer to structure of fpga bridge ops
+ * @priv: FPGA bridge private data
+ *
+ * Return: 0 for success, error code otherwise.
+ */
+int fpga_bridge_register(struct device_d *dev, const char *name,
+ const struct fpga_bridge_ops *br_ops, void *priv)
+{
+ struct fpga_bridge *bridge;
+ struct param_d *p;
+ int ret = 0;
+
+ if (!name || !strlen(name)) {
+ dev_err(dev, "Attempt to register with no name!\n");
+ return -EINVAL;
+ }
+
+ bridge = xzalloc(sizeof(*bridge));
+ if (!bridge)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&bridge->node);
+
+ bridge->br_ops = br_ops;
+ bridge->priv = priv;
+
+ bridge->dev.parent = dev;
+ bridge->dev.device_node = dev->device_node;
+ bridge->dev.id = DEVICE_ID_DYNAMIC;
+
+ bridge->dev.name = xstrdup(name);
+
+ ret = register_device(&bridge->dev);
+ if (ret)
+ goto out;
+
+ dev->priv = bridge;
+
+ bridge->enable = 0;
+ p = dev_add_param_bool(&bridge->dev, "enable", set_enable,
+ NULL, &bridge->enable, bridge);
+ if (IS_ERR(p))
+ return PTR_ERR(p);
+
+ of_platform_populate(dev->device_node, NULL, dev);
+
+ dev_info(bridge->dev.parent, "fpga bridge [%s] registered\n",
+ bridge->dev.name);
+
+ return 0;
+
+out:
+ kfree(bridge);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(fpga_bridge_register);
diff --git a/drivers/fpga/socfpga-fpga2sdram-bridge.c b/drivers/fpga/socfpga-fpga2sdram-bridge.c
new file mode 100644
index 0000000000..a9760597dd
--- /dev/null
+++ b/drivers/fpga/socfpga-fpga2sdram-bridge.c
@@ -0,0 +1,139 @@
+/*
+ * FPGA to SDRAM Bridge Driver for Altera SoCFPGA Devices
+ *
+ * Copyright (C) 2013-2016 Altera Corporation, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This driver manages a bridge between an FPGA and the SDRAM used by the ARM
+ * host processor system (HPS).
+ *
+ * The bridge contains 4 read ports, 4 write ports, and 6 command ports.
+ * Reconfiguring these ports requires that no SDRAM transactions occur during
+ * reconfiguration. The code reconfiguring the ports cannot run out of SDRAM
+ * nor can the FPGA access the SDRAM during reconfiguration. This driver does
+ * not support reconfiguring the ports. The ports are configured by code
+ * running out of on chip ram before Linux is started and the configuration
+ * is passed in a handoff register in the system manager.
+ *
+ * This driver supports enabling and disabling of the configured ports, which
+ * allows for safe reprogramming of the FPGA, assuming that the new FPGA image
+ * uses the same port configuration. Bridges must be disabled before
+ * reprogramming the FPGA and re-enabled after the FPGA has been programmed.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <fpga-bridge.h>
+#include <mfd/syscon.h>
+#include <of_device.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+
+#define SOCFPGA_SDRCTL_ADDR 0xffc25000
+#define ALT_SDR_CTL_FPGAPORTRST_OFST 0x80
+#define ALT_SDR_CTL_FPGAPORTRST_PORTRSTN_MSK 0x00003fff
+#define ALT_SDR_CTL_FPGAPORTRST_RD_SHIFT 0
+#define ALT_SDR_CTL_FPGAPORTRST_WR_SHIFT 4
+#define ALT_SDR_CTL_FPGAPORTRST_CTRL_SHIFT 8
+
+#define SOCFPGA_SYSMGR_ADDR 0xffd08000
+/*
+ * From the Cyclone V HPS Memory Map document:
+ * These registers are used to store handoff information between the
+ * preloader and the OS. These 8 registers can be used to store any
+ * information. The contents of these registers have no impact on
+ * the state of the HPS hardware.
+ */
+#define SYSMGR_ISWGRP_HANDOFF3 (0x8C)
+
+#define F2S_BRIDGE_NAME "fpga2sdram"
+
+struct alt_fpga2sdram_data {
+ struct device_d *dev;
+ int mask;
+};
+
+static inline int _alt_fpga2sdram_enable_set(struct alt_fpga2sdram_data *priv,
+ bool enable)
+{
+ int val;
+
+ val = readl(SOCFPGA_SDRCTL_ADDR + ALT_SDR_CTL_FPGAPORTRST_OFST);
+
+ if (enable)
+ val |= priv->mask;
+ else
+ val = 0;
+
+ /* The kernel driver expects this value in this register :-( */
+ writel(priv->mask, SOCFPGA_SYSMGR_ADDR + SYSMGR_ISWGRP_HANDOFF3);
+
+ dev_dbg(priv->dev, "setting fpgaportrst to 0x%08x\n", val);
+
+ return writel(val, SOCFPGA_SDRCTL_ADDR + ALT_SDR_CTL_FPGAPORTRST_OFST);
+}
+
+static int alt_fpga2sdram_enable_set(struct fpga_bridge *bridge, bool enable)
+{
+ return _alt_fpga2sdram_enable_set(bridge->priv, enable);
+}
+
+struct prop_map {
+ char *prop_name;
+ u32 *prop_value;
+ u32 prop_max;
+};
+
+static const struct fpga_bridge_ops altera_fpga2sdram_br_ops = {
+ .enable_set = alt_fpga2sdram_enable_set,
+};
+
+static struct of_device_id altera_fpga_of_match[] = {
+ { .compatible = "altr,socfpga-fpga2sdram-bridge" },
+ {},
+};
+
+static int alt_fpga_bridge_probe(struct device_d *dev)
+{
+ struct alt_fpga2sdram_data *priv;
+ int ret = 0;
+
+ priv = xzalloc(sizeof(*priv));
+ if (!priv)
+ return -ENOMEM;
+
+ /* enable all ports for now */
+ priv->mask = ALT_SDR_CTL_FPGAPORTRST_PORTRSTN_MSK;
+
+ priv->dev = dev;
+
+ ret = fpga_bridge_register(dev, F2S_BRIDGE_NAME,
+ &altera_fpga2sdram_br_ops, priv);
+ if (ret)
+ return ret;
+
+ dev_info(dev, "driver initialized with handoff %08x\n", priv->mask);
+
+ return ret;
+}
+
+static struct driver_d altera_fpga_driver = {
+ .probe = alt_fpga_bridge_probe,
+ .name = "altera-fpga2sdram-bridge",
+ .of_compatible = DRV_OF_COMPAT(altera_fpga_of_match),
+};
+device_platform_driver(altera_fpga_driver);
diff --git a/drivers/fpga/socfpga-hps2fpga-bridge.c b/drivers/fpga/socfpga-hps2fpga-bridge.c
new file mode 100644
index 0000000000..ecb33ea0b3
--- /dev/null
+++ b/drivers/fpga/socfpga-hps2fpga-bridge.c
@@ -0,0 +1,179 @@
+/*
+ * FPGA to/from HPS Bridge Driver for Altera SoCFPGA Devices
+ *
+ * Copyright (C) 2013-2016 Altera Corporation, All Rights Reserved.
+ *
+ * Includes this patch from the mailing list:
+ * fpga: altera-hps2fpga: fix HPS2FPGA bridge visibility to L3 masters
+ * Signed-off-by: Anatolij Gustschin <agust@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This driver manages bridges on a Altera SOCFPGA between the ARM host
+ * processor system (HPS) and the embedded FPGA.
+ *
+ * This driver supports enabling and disabling of the configured ports, which
+ * allows for safe reprogramming of the FPGA, assuming that the new FPGA image
+ * uses the same port configuration. Bridges must be disabled before
+ * reprogramming the FPGA and re-enabled after the FPGA has been programmed.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <fpga-bridge.h>
+#include <mfd/syscon.h>
+#include <of_device.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+
+#define SOCFPGA_L3_ADDR 0xff800000
+#define ALT_L3_REMAP_OFST 0x0
+#define ALT_L3_REMAP_MPUZERO_MSK 0x00000001
+#define ALT_L3_REMAP_H2F_MSK 0x00000008
+#define ALT_L3_REMAP_LWH2F_MSK 0x00000010
+
+#define HPS2FPGA_BRIDGE_NAME "hps2fpga"
+#define LWHPS2FPGA_BRIDGE_NAME "lwhps2fpga"
+#define FPGA2HPS_BRIDGE_NAME "fpga2hps"
+
+struct altera_hps2fpga_data {
+ struct device_d *dev;
+ const char *name;
+ struct reset_control *bridge_reset;
+ unsigned int remap_mask;
+ struct clk *clk;
+};
+
+/* The L3 REMAP register is write only, so keep a cached value. */
+static unsigned int l3_remap_shadow;
+
+static int _alt_hps2fpga_enable_set(struct altera_hps2fpga_data *priv,
+ bool enable)
+{
+ int ret;
+
+ /* bring bridge out of reset */
+ if (enable)
+ ret = reset_control_deassert(priv->bridge_reset);
+ else
+ ret = reset_control_assert(priv->bridge_reset);
+ if (ret)
+ return ret;
+
+ /* Allow bridge to be visible to L3 masters or not */
+ if (priv->remap_mask) {
+ l3_remap_shadow |= ALT_L3_REMAP_MPUZERO_MSK;
+
+ if (enable)
+ l3_remap_shadow |= priv->remap_mask;
+ else
+ l3_remap_shadow &= ~priv->remap_mask;
+
+ dev_dbg(priv->dev, "setting L3 visibility to 0x%08x\n",
+ l3_remap_shadow);
+
+ writel(l3_remap_shadow, SOCFPGA_L3_ADDR + ALT_L3_REMAP_OFST);
+ }
+
+ return ret;
+}
+
+static int alt_hps2fpga_enable_set(struct fpga_bridge *bridge, bool enable)
+{
+ return _alt_hps2fpga_enable_set(bridge->priv, enable);
+}
+
+static const struct fpga_bridge_ops altera_hps2fpga_br_ops = {
+ .enable_set = alt_hps2fpga_enable_set,
+};
+
+static struct altera_hps2fpga_data hps2fpga_data = {
+ .name = HPS2FPGA_BRIDGE_NAME,
+ .remap_mask = ALT_L3_REMAP_H2F_MSK,
+};
+
+static struct altera_hps2fpga_data lwhps2fpga_data = {
+ .name = LWHPS2FPGA_BRIDGE_NAME,
+ .remap_mask = ALT_L3_REMAP_LWH2F_MSK,
+};
+
+static struct altera_hps2fpga_data fpga2hps_data = {
+ .name = FPGA2HPS_BRIDGE_NAME,
+};
+
+static struct of_device_id altera_fpga_of_match[] = {
+ { .compatible = "altr,socfpga-hps2fpga-bridge",
+ .data = &hps2fpga_data },
+ { .compatible = "altr,socfpga-lwhps2fpga-bridge",
+ .data = &lwhps2fpga_data },
+ { .compatible = "altr,socfpga-fpga2hps-bridge",
+ .data = &fpga2hps_data },
+ { /* sentinel */ },
+};
+
+static int alt_fpga_bridge_probe(struct device_d *dev)
+{
+ struct altera_hps2fpga_data *priv;
+ const struct of_device_id *of_id;
+ u32 enable;
+ int ret;
+
+ of_id = of_match_device(altera_fpga_of_match, dev);
+ priv = (struct altera_hps2fpga_data *)of_id->data;
+
+ priv->bridge_reset = of_reset_control_get(dev->device_node, NULL);
+ if (IS_ERR(priv->bridge_reset)) {
+ dev_err(dev, "Could not get %s reset control\n", priv->name);
+ return PTR_ERR(priv->bridge_reset);
+ }
+
+ priv->clk = clk_get(dev, NULL);
+ if (IS_ERR(priv->clk)) {
+ dev_err(dev, "no clock specified\n");
+ return PTR_ERR(priv->clk);
+ }
+
+ ret = clk_enable(priv->clk);
+ if (ret) {
+ dev_err(dev, "could not enable clock\n");
+ return -EBUSY;
+ }
+
+ priv->dev = dev;
+
+ if (!of_property_read_u32(dev->device_node, "bridge-enable", &enable)) {
+ if (enable > 1) {
+ dev_warn(dev, "invalid bridge-enable %u > 1\n", enable);
+ } else {
+ dev_info(dev, "%s bridge\n",
+ (enable ? "enabling" : "disabling"));
+
+ ret = _alt_hps2fpga_enable_set(priv, enable);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return fpga_bridge_register(dev, priv->name, &altera_hps2fpga_br_ops,
+ priv);
+}
+
+static struct driver_d alt_fpga_bridge_driver = {
+ .probe = alt_fpga_bridge_probe,
+ .name = "altera-hps2fpga-bridge",
+ .of_compatible = DRV_OF_COMPAT(altera_fpga_of_match),
+};
+device_platform_driver(alt_fpga_bridge_driver);
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 23be25d85d..e58fe50f70 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -49,6 +49,7 @@ config OF_BAREBOX_ENV_IN_FS
config OF_OVERLAY
select OFTREE
+ select FIRMWARE
bool "Devicetree overlays"
help
Overlays allow to patch the devicetree. Unlike Linux, Barebox does
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 5c39d4bcef..0ae9a845a4 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -1742,7 +1742,7 @@ int barebox_register_fdt(const void *dtb)
if (root_node)
return -EBUSY;
- root = of_unflatten_dtb(dtb);
+ root = of_unflatten_dtb(dtb, INT_MAX);
if (IS_ERR(root)) {
pr_err("Cannot unflatten dtb: %pe\n", root);
return PTR_ERR(root);
@@ -2422,6 +2422,7 @@ struct device_node *of_copy_node(struct device_node *parent, const struct device
struct property *pp;
np = of_new_node(parent, other->name);
+ np->phandle = other->phandle;
list_for_each_entry(pp, &other->properties, list)
of_new_property(np, pp->name, pp->value, pp->length);
@@ -2432,6 +2433,11 @@ struct device_node *of_copy_node(struct device_node *parent, const struct device
return np;
}
+struct device_node *of_dup(const struct device_node *root)
+{
+ return of_copy_node(NULL, root);
+}
+
void of_delete_node(struct device_node *node)
{
struct device_node *n, *nt;
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index d98913e54a..f72f5e3a30 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -114,7 +114,8 @@ static int of_unflatten_reservemap(struct device_node *root,
* Parse a flat device tree binary blob and return a pointer to the
* unflattened tree.
*/
-static struct device_node *__of_unflatten_dtb(const void *infdt, bool constprops)
+static struct device_node *__of_unflatten_dtb(const void *infdt, int size,
+ bool constprops)
{
const void *nodep; /* property node pointer */
uint32_t tag; /* tag */
@@ -131,6 +132,9 @@ static struct device_node *__of_unflatten_dtb(const void *infdt, bool constprops
unsigned int maxlen;
const struct fdt_header *fdt = infdt;
+ if (size < sizeof(struct fdt_header))
+ return ERR_PTR(-EINVAL);
+
if (fdt->magic != cpu_to_fdt32(FDT_MAGIC)) {
pr_err("bad magic: 0x%08x\n", fdt32_to_cpu(fdt->magic));
return ERR_PTR(-EINVAL);
@@ -147,6 +151,9 @@ static struct device_node *__of_unflatten_dtb(const void *infdt, bool constprops
f.off_dt_strings = fdt32_to_cpu(fdt->off_dt_strings);
f.size_dt_strings = fdt32_to_cpu(fdt->size_dt_strings);
+ if (f.totalsize > size)
+ return ERR_PTR(-EINVAL);
+
if (f.off_dt_struct + f.size_dt_struct > f.totalsize) {
pr_err("unflatten: dt size exceeds total size\n");
return ERR_PTR(-ESPIPE);
@@ -274,9 +281,9 @@ err:
* Parse a flat device tree binary blob and return a pointer to the unflattened
* tree. The tree must be freed after use with of_delete_node().
*/
-struct device_node *of_unflatten_dtb(const void *infdt)
+struct device_node *of_unflatten_dtb(const void *infdt, int size)
{
- return __of_unflatten_dtb(infdt, false);
+ return __of_unflatten_dtb(infdt, size, false);
}
/**
@@ -290,9 +297,9 @@ struct device_node *of_unflatten_dtb(const void *infdt)
* whole lifetime of the returned tree. This is normally not what you want, so
* use of_unflatten_dtb() instead.
*/
-struct device_node *of_unflatten_dtb_const(const void *infdt)
+struct device_node *of_unflatten_dtb_const(const void *infdt, int size)
{
- return __of_unflatten_dtb(infdt, true);
+ return __of_unflatten_dtb(infdt, size, true);
}
struct fdt {
diff --git a/drivers/of/of_firmware.c b/drivers/of/of_firmware.c
index 096f84572e..687e675302 100644
--- a/drivers/of/of_firmware.c
+++ b/drivers/of/of_firmware.c
@@ -6,34 +6,34 @@
#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);
- }
+ 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;
}
+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 *data)
+ struct device_node *fragment, void *unused)
{
- struct overlay_info *info = data;
const char *firmware_name;
- const char *firmware_path = info->firmware_path;
- char *firmware;
int err;
struct firmware_mgr *mgr;
+ struct fw_load_entry *fle;
err = of_property_read_string(fragment,
"firmware-name", &firmware_name);
@@ -46,46 +46,93 @@ static int load_firmware(struct device_node *target,
if (!target)
return -EINVAL;
+ if (!of_device_is_compatible(target, "fpga-region"))
+ return 0;
+
mgr = of_node_get_mgr(target);
if (!mgr)
return -EINVAL;
- firmware = basprintf("%s/%s", firmware_path, firmware_name);
- if (!firmware)
- return -ENOMEM;
+ fle = xzalloc(sizeof(*fle));
+ fle->mgr = mgr;
+ fle->firmware = xstrdup(firmware_name);
- err = firmwaremgr_load_file(mgr, firmware);
+ list_add_tail(&fle->list, &fw_load_list);
- free(firmware);
+ return 0;
+}
- return err;
+/*
+ * 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)
+{
+ return of_process_overlay(root, overlay, load_firmware, NULL);
}
-int of_firmware_load_overlay(struct device_node *overlay, const char *path)
+/**
+ * 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 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();
- 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, &info);
-
- if (resolved)
- of_delete_node(resolved);
-
- return err;
+ 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);
+ }
}
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
index 8f4ee3f0a2..42b309805f 100644
--- a/drivers/of/overlay.c
+++ b/drivers/of/overlay.c
@@ -11,6 +11,13 @@
#include <common.h>
#include <of.h>
#include <errno.h>
+#include <globalvar.h>
+#include <magicvar.h>
+#include <string.h>
+#include <libfile.h>
+#include <fs.h>
+#include <libbb.h>
+#include <fnmatch.h>
static struct device_node *find_target(struct device_node *root,
struct device_node *fragment)
@@ -160,6 +167,8 @@ static int of_overlay_apply_fragment(struct device_node *root,
return of_overlay_apply(target, overlay);
}
+static char *of_overlay_compatible;
+
/**
* Apply the overlay on the passed devicetree root
* @root: the devicetree onto which the overlay will be applied
@@ -176,6 +185,10 @@ int of_overlay_apply_tree(struct device_node *root,
if (!resolved)
return -EINVAL;
+ err = of_overlay_pre_load_firmware(root, resolved);
+ if (err)
+ goto out_err;
+
/* Copy symbols from resolved overlay to base device tree */
of_overlay_apply_symbols(root, resolved);
@@ -186,11 +199,140 @@ int of_overlay_apply_tree(struct device_node *root,
pr_warn("failed to apply %s\n", fragment->name);
}
+out_err:
of_delete_node(resolved);
return err;
}
+static char *of_overlay_filter;
+
+static LIST_HEAD(of_overlay_filters);
+
+static struct of_overlay_filter *of_overlay_find_filter(const char *name)
+{
+ struct of_overlay_filter *f;
+
+ list_for_each_entry(f, &of_overlay_filters, list)
+ if (!strcmp(f->name, name))
+ return f;
+ return NULL;
+}
+
+static bool of_overlay_matches_filter(const char *filename, struct device_node *ovl)
+{
+ struct of_overlay_filter *filter;
+ char *p, *path, *n;
+ bool apply = false;
+ bool have_filename_filter = false;
+ bool have_content_filter = false;
+
+ p = path = strdup(of_overlay_filter);
+
+ while ((n = strsep_unescaped(&p, " "))) {
+ int score = 0;
+
+ if (!*n)
+ continue;
+
+ filter = of_overlay_find_filter(n);
+ if (!filter) {
+ pr_err("Ignoring unknown filter %s\n", n);
+ continue;
+ }
+
+ if (filter->filter_filename)
+ have_filename_filter = true;
+ if (filter->filter_content)
+ have_content_filter = true;
+
+ if (filename) {
+ if (filter->filter_filename &&
+ filter->filter_filename(filter, kbasename(filename)))
+ score++;
+ } else {
+ score++;
+ }
+
+ if (ovl) {
+ if (filter->filter_content &&
+ filter->filter_content(filter, ovl))
+ score++;
+ } else {
+ score++;
+ }
+
+ if (score == 2) {
+ apply = true;
+ break;
+ }
+ }
+
+ free(path);
+
+ /* No filter found at all, no match */
+ if (!have_filename_filter && !have_content_filter)
+ return false;
+
+ /* Want to match filename, but we do not have a filename_filter */
+ if (filename && !have_filename_filter)
+ return true;
+
+ /* Want to match content, but we do not have a content_filter */
+ if (ovl && !have_content_filter)
+ return true;
+
+ if (apply)
+ pr_debug("filename %s, overlay %p: match against filter %s\n",
+ filename ?: "<NONE>",
+ ovl, filter->name);
+ else
+ pr_debug("filename %s, overlay %p: no match\n",
+ filename ?: "<NONE>", ovl);
+
+ return apply;
+}
+
+int of_overlay_apply_file(struct device_node *root, const char *filename,
+ bool filter)
+{
+ void *fdt;
+ struct device_node *ovl;
+ size_t size;
+ int ret;
+
+ if (filter && !of_overlay_matches_filter(filename, NULL))
+ return 0;
+
+ ret = read_file_2(filename, &size, &fdt, FILESIZE_MAX);
+ if (ret)
+ return ret;
+
+ ovl = of_unflatten_dtb(fdt, size);
+
+ free(fdt);
+
+ if (IS_ERR(ovl)) {
+ pr_err("Failed to unflatten %s: %pe\n", filename, ovl);
+ return PTR_ERR(ovl);
+ }
+
+ if (filter && !of_overlay_matches_filter(NULL, ovl))
+ return 0;
+
+ ret = of_overlay_apply_tree(root, ovl);
+ if (ret == -ENODEV)
+ pr_debug("Not applied %s (not compatible)\n", filename);
+ else if (ret)
+ pr_err("Cannot apply %s: %s\n", filename, strerror(-ret));
+ else
+ pr_info("Applied %s\n", filename);
+
+ of_delete_node(ovl);
+
+ return ret;
+}
+
static int of_overlay_fixup(struct device_node *root, void *data)
{
struct device_node *overlay = data;
@@ -250,3 +392,216 @@ int of_register_overlay(struct device_node *overlay)
{
return of_register_fixup(of_overlay_fixup, overlay);
}
+
+static char *of_overlay_filepattern;
+static char *of_overlay_dir;
+static char *of_overlay_basedir;
+
+/**
+ * of_overlay_set_basedir - set the overlay basedir
+ * @path: The new overlay basedir
+ *
+ * This specifies the base directory where overlay files are expected. By
+ * default this is the root directory, but it is overwritten by blspec to
+ * point to the rootfs of the about-to-be-booted system.
+ */
+void of_overlay_set_basedir(const char *path)
+{
+ free(of_overlay_basedir);
+ of_overlay_basedir = strdup(path);
+}
+
+static int of_overlay_apply_dir(struct device_node *root, const char *dirname,
+ bool filter)
+{
+ char *p, *path;
+ int ret = 0;
+ DIR *dir;
+
+ if (!dirname || !*dirname)
+ return 0;
+
+ pr_debug("Applying overlays from %s\n", dirname);
+
+ dir = opendir(dirname);
+ if (!dir)
+ return -errno;
+
+ p = path = strdup(of_overlay_filepattern);
+
+ while (1) {
+ struct dirent *ent;
+ char *filename;
+
+ ent = readdir(dir);
+ if (!ent)
+ break;
+
+ if (!strcmp(dir->d.d_name, ".") || !strcmp(dir->d.d_name, ".."))
+ continue;
+
+ filename = basprintf("%s/%s", dirname, dir->d.d_name);
+
+ of_overlay_apply_file(root, filename, filter);
+
+ free(filename);
+ }
+
+ closedir(dir);
+
+ return ret;
+}
+
+static int of_overlay_global_fixup(struct device_node *root, void *data)
+{
+ char *dir;
+ int ret;
+
+ if (*of_overlay_dir == '/')
+ return of_overlay_apply_dir(root, of_overlay_dir, true);
+
+ dir = concat_path_file(of_overlay_basedir, of_overlay_dir);
+
+ ret = of_overlay_apply_dir(root, dir, true);
+
+ free(dir);
+
+ return ret;
+}
+
+/**
+ * of_overlay_register_filter - register a new overlay filter
+ * @filter: The new filter
+ *
+ * Register a new overlay filter. A filter can either match on
+ * the filename or on the content of an overlay, but not on both.
+ * If that's desired two filters have to be registered.
+ *
+ * @return: 0 for success, negative error code otherwise
+ */
+int of_overlay_register_filter(struct of_overlay_filter *filter)
+{
+ if (filter->filter_filename && filter->filter_content)
+ return -EINVAL;
+
+ list_add_tail(&filter->list, &of_overlay_filters);
+
+ return 0;
+}
+
+/**
+ * of_overlay_filter_filename - A filter that matches on the filename of
+ * an overlay
+ * @f: The filter
+ * @filename: The filename of the overlay
+ *
+ * This filter matches when the filename matches one of the patterns given
+ * in global.of.overlay.filepattern. global.of.overlay.filepattern shall
+ * contain a space separated list of wildcard patterns.
+ *
+ * @return: True when the overlay shall be applied, false otherwise.
+ */
+static bool of_overlay_filter_filename(struct of_overlay_filter *f,
+ const char *filename)
+{
+ char *p, *path, *n;
+ int ret;
+ bool apply;
+
+ p = path = strdup(of_overlay_filepattern);
+
+ while ((n = strsep_unescaped(&p, " "))) {
+ if (!*n)
+ continue;
+
+ ret = fnmatch(n, filename, 0);
+
+ if (!ret) {
+ apply = true;
+ goto out;
+ }
+ }
+
+ apply = false;
+out:
+ free(path);
+
+ return apply;
+}
+
+static struct of_overlay_filter of_overlay_filepattern_filter = {
+ .name = "filepattern",
+ .filter_filename = of_overlay_filter_filename,
+};
+
+/**
+ * of_overlay_filter_compatible - A filter that matches on the compatible of
+ * an overlay
+ * @f: The filter
+ * @ovl: The overlay
+ *
+ * This filter matches when the compatible of an overlay matches to one
+ * of the compatibles given in global.of.overlay.compatible. When the
+ * overlay doesn't contain a compatible entry it is considered matching.
+ * Also when no compatibles are given in global.of.overlay.compatible
+ * all overlays will match.
+ *
+ * @return: True when the overlay shall be applied, false otherwise.
+ */
+static bool of_overlay_filter_compatible(struct of_overlay_filter *f,
+ struct device_node *ovl)
+{
+ char *p, *n, *compatibles;
+ bool res = false;
+
+ if (!of_overlay_compatible || !*of_overlay_compatible)
+ return true;
+ if (!of_find_property(ovl, "compatible", NULL))
+ return true;
+
+ p = compatibles = xstrdup(of_overlay_compatible);
+
+ while ((n = strsep_unescaped(&p, " "))) {
+ if (!*n)
+ continue;
+
+ if (of_device_is_compatible(ovl, n)) {
+ res = true;
+ break;
+ }
+ }
+
+ free(compatibles);
+
+ return res;
+}
+
+static struct of_overlay_filter of_overlay_compatible_filter = {
+ .name = "compatible",
+ .filter_content = of_overlay_filter_compatible,
+};
+
+static int of_overlay_init(void)
+{
+ of_overlay_filepattern = strdup("*");
+ of_overlay_filter = strdup("filepattern compatible");
+ of_overlay_set_basedir("/");
+
+ globalvar_add_simple_string("of.overlay.compatible", &of_overlay_compatible);
+ globalvar_add_simple_string("of.overlay.filepattern", &of_overlay_filepattern);
+ globalvar_add_simple_string("of.overlay.filter", &of_overlay_filter);
+ globalvar_add_simple_string("of.overlay.dir", &of_overlay_dir);
+
+ of_overlay_register_filter(&of_overlay_filepattern_filter);
+ of_overlay_register_filter(&of_overlay_compatible_filter);
+
+ of_register_fixup(of_overlay_global_fixup, NULL);
+
+ return 0;
+}
+device_initcall(of_overlay_init);
+
+BAREBOX_MAGICVAR(global.of.overlay.compatible, "space separated list of compatibles an overlay must match");
+BAREBOX_MAGICVAR(global.of.overlay.filepattern, "space separated list of filepatterns an overlay must match");
+BAREBOX_MAGICVAR(global.of.overlay.dir, "Directory to look for dt overlays");
+BAREBOX_MAGICVAR(global.of.overlay.filter, "space separated list of filters");
diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index e2edca6658..1dd071635f 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -147,7 +147,7 @@ EXPORT_SYMBOL_GPL(reset_control_deassert);
*
* Use of id names is optional.
*/
-static struct reset_control *of_reset_control_get(struct device_node *node,
+struct reset_control *of_reset_control_get(struct device_node *node,
const char *id)
{
struct reset_control *rstc;
diff --git a/include/filetype.h b/include/filetype.h
index fd339f9564..ae0920320e 100644
--- a/include/filetype.h
+++ b/include/filetype.h
@@ -70,6 +70,20 @@ enum filetype is_fat_or_mbr(const unsigned char *sector, unsigned long *bootsec)
int is_fat_boot_sector(const void *_buf);
bool filetype_is_barebox_image(enum filetype ft);
+static inline bool file_is_compressed_file(enum filetype ft)
+{
+ switch (ft) {
+ case filetype_lzo_compressed:
+ case filetype_lz4_compressed:
+ case filetype_gzip:
+ case filetype_bzip2:
+ case filetype_xz_compressed:
+ return true;
+ default:
+ return false;
+ }
+}
+
#define ARM_HEAD_SIZE 0x30
#define ARM_HEAD_MAGICWORD_OFFSET 0x20
#define ARM_HEAD_SIZE_OFFSET 0x2C
diff --git a/include/firmware.h b/include/firmware.h
index 19777d9bf7..0ffea52840 100644
--- a/include/firmware.h
+++ b/include/firmware.h
@@ -13,6 +13,8 @@ struct firmware_handler {
char *id; /* unique identifier for this firmware device */
char *model; /* description for this device */
struct device_d *dev;
+ void *priv;
+ struct device_node *device_node;
/* called once to prepare the firmware's programming cycle */
int (*open)(struct firmware_handler*);
/* called multiple times to program the firmware with the given data */
@@ -27,25 +29,34 @@ int firmwaremgr_register(struct firmware_handler *);
struct firmware_mgr *firmwaremgr_find(const char *);
#ifdef CONFIG_FIRMWARE
-struct firmware_mgr *firmwaremgr_find_by_node(const struct device_node *np);
+struct firmware_mgr *firmwaremgr_find_by_node(struct device_node *np);
+int firmwaremgr_load_file(struct firmware_mgr *, const char *path);
+const char *firmware_get_searchpath(void);
+void firmware_set_searchpath(const char *path);
#else
-static inline struct firmware_mgr *firmwaremgr_find_by_node(const struct device_node *np)
+static inline struct firmware_mgr *firmwaremgr_find_by_node(struct device_node *np)
{
return NULL;
}
-#endif
-void firmwaremgr_list_handlers(void);
-
-#ifdef CONFIG_FIRMWARE
-int firmwaremgr_load_file(struct firmware_mgr *, const char *path);
-#else
static inline int firmwaremgr_load_file(struct firmware_mgr *mgr, const char *path)
{
return -ENOSYS;
}
+
+static inline const char *firmware_get_searchpath(void)
+{
+ return NULL;
+}
+
+static inline void firmware_set_searchpath(const char *path)
+{
+}
+
#endif
+void firmwaremgr_list_handlers(void);
+
#define get_builtin_firmware(name, start, size) \
{ \
extern char _fw_##name##_start[]; \
diff --git a/include/fpga-bridge.h b/include/fpga-bridge.h
new file mode 100644
index 0000000000..fef2a9ccbb
--- /dev/null
+++ b/include/fpga-bridge.h
@@ -0,0 +1,74 @@
+#include <common.h>
+
+#ifndef _LINUX_FPGA_BRIDGE_H
+#define _LINUX_FPGA_BRIDGE_H
+
+struct fpga_bridge;
+
+/**
+ * struct fpga_bridge_ops - ops for low level FPGA bridge drivers
+ * @enable_show: returns the FPGA bridge's status
+ * @enable_set: set a FPGA bridge as enabled or disabled
+ * @fpga_bridge_remove: set FPGA into a specific state during driver remove
+ */
+struct fpga_bridge_ops {
+ int (*enable_show)(struct fpga_bridge *bridge);
+ int (*enable_set)(struct fpga_bridge *bridge, bool enable);
+ void (*fpga_bridge_remove)(struct fpga_bridge *bridge);
+};
+
+/**
+ * struct fpga_bridge - FPGA bridge structure
+ * @name: name of low level FPGA bridge
+ * @dev: FPGA bridge device
+ * @mutex: enforces exclusive reference to bridge
+ * @br_ops: pointer to struct of FPGA bridge ops
+ * @info: fpga image specific information
+ * @node: FPGA bridge list node
+ * @priv: low level driver private date
+ */
+struct fpga_bridge {
+ struct device_d dev;
+ const struct fpga_bridge_ops *br_ops;
+ struct list_head node;
+ unsigned int enable;
+ void *priv;
+};
+
+#define to_fpga_bridge(d) container_of(d, struct fpga_bridge, dev)
+
+struct fpga_bridge *of_fpga_bridge_get(struct device_node *node);
+void fpga_bridge_put(struct fpga_bridge *bridge);
+int fpga_bridge_enable(struct fpga_bridge *bridge);
+int fpga_bridge_disable(struct fpga_bridge *bridge);
+
+#ifdef CONFIG_FPGA_BRIDGE
+int fpga_bridges_enable(struct list_head *bridge_list);
+int fpga_bridges_disable(struct list_head *bridge_list);
+int fpga_bridge_get_to_list(struct device_node *np,
+ struct list_head *bridge_list);
+void fpga_bridges_put(struct list_head *bridge_list);
+#else
+static inline int fpga_bridges_enable(struct list_head *bridge_list)
+{
+ return -ENOSYS;
+};
+static inline int fpga_bridges_disable(struct list_head *bridge_list)
+{
+ return -ENOSYS;
+};
+static inline int fpga_bridge_get_to_list(struct device_node *np,
+ struct list_head *bridge_list)
+{
+ return -ENOSYS;
+};
+static inline void fpga_bridges_put(struct list_head *bridge_list)
+{
+ return;
+};
+#endif
+
+int fpga_bridge_register(struct device_d *dev, const char *name,
+ const struct fpga_bridge_ops *br_ops, void *priv);
+
+#endif /* _LINUX_FPGA_BRIDGE_H */
diff --git a/include/fpga-mgr.h b/include/fpga-mgr.h
new file mode 100644
index 0000000000..a120b39189
--- /dev/null
+++ b/include/fpga-mgr.h
@@ -0,0 +1,102 @@
+/*
+ * FPGA Framework
+ *
+ * Copyright (C) 2013-2015 Altera Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _LINUX_FPGA_MGR_H
+#define _LINUX_FPGA_MGR_H
+
+#include <firmware.h>
+
+struct fpga_manager;
+
+/**
+ * enum fpga_mgr_states - fpga framework states
+ * @FPGA_MGR_STATE_UNKNOWN: can't determine state
+ * @FPGA_MGR_STATE_POWER_OFF: FPGA power is off
+ * @FPGA_MGR_STATE_POWER_UP: FPGA reports power is up
+ * @FPGA_MGR_STATE_RESET: FPGA in reset state
+ * @FPGA_MGR_STATE_FIRMWARE_REQ: firmware request in progress
+ * @FPGA_MGR_STATE_FIRMWARE_REQ_ERR: firmware request failed
+ * @FPGA_MGR_STATE_WRITE_INIT: preparing FPGA for programming
+ * @FPGA_MGR_STATE_WRITE_INIT_ERR: Error during WRITE_INIT stage
+ * @FPGA_MGR_STATE_WRITE: writing image to FPGA
+ * @FPGA_MGR_STATE_WRITE_ERR: Error while writing FPGA
+ * @FPGA_MGR_STATE_WRITE_COMPLETE: Doing post programming steps
+ * @FPGA_MGR_STATE_WRITE_COMPLETE_ERR: Error during WRITE_COMPLETE
+ * @FPGA_MGR_STATE_OPERATING: FPGA is programmed and operating
+ */
+enum fpga_mgr_states {
+ /* default FPGA states */
+ FPGA_MGR_STATE_UNKNOWN,
+ FPGA_MGR_STATE_POWER_OFF,
+ FPGA_MGR_STATE_POWER_UP,
+ FPGA_MGR_STATE_RESET,
+
+ /* getting an image for loading */
+ FPGA_MGR_STATE_FIRMWARE_REQ,
+ FPGA_MGR_STATE_FIRMWARE_REQ_ERR,
+
+ /* write sequence: init, write, complete */
+ FPGA_MGR_STATE_WRITE_INIT,
+ FPGA_MGR_STATE_WRITE_INIT_ERR,
+ FPGA_MGR_STATE_WRITE,
+ FPGA_MGR_STATE_WRITE_ERR,
+ FPGA_MGR_STATE_WRITE_COMPLETE,
+ FPGA_MGR_STATE_WRITE_COMPLETE_ERR,
+
+ /* fpga is programmed and operating */
+ FPGA_MGR_STATE_OPERATING,
+};
+
+/*
+ * FPGA Manager flags
+ * FPGA_MGR_PARTIAL_RECONFIG: do partial reconfiguration if supported
+ * FPGA_MGR_EXTERNAL_CONFIG: FPGA has been configured prior to Linux booting
+ * FPGA_MGR_BITSTREAM_LSB_FIRST: SPI bitstream bit order is LSB first
+ * FPGA_MGR_COMPRESSED_BITSTREAM: FPGA bitstream is compressed
+ */
+#define FPGA_MGR_PARTIAL_RECONFIG BIT(0)
+#define FPGA_MGR_EXTERNAL_CONFIG BIT(1)
+#define FPGA_MGR_ENCRYPTED_BITSTREAM BIT(2)
+#define FPGA_MGR_BITSTREAM_LSB_FIRST BIT(3)
+#define FPGA_MGR_COMPRESSED_BITSTREAM BIT(4)
+
+/**
+ * struct fpga_image_info - information specific to a FPGA image
+ * @flags: boolean flags as defined above
+ * @enable_timeout_us: maximum time to enable traffic through bridge (uSec)
+ * @disable_timeout_us: maximum time to disable traffic through bridge (uSec)
+ * @config_complete_timeout_us: maximum time for FPGA to switch to operating
+ * status in the write_complete op.
+ */
+struct fpga_image_info {
+ u32 flags;
+ u32 enable_timeout_us;
+ u32 disable_timeout_us;
+ u32 config_complete_timeout_us;
+};
+
+struct fpgamgr {
+ struct firmware_handler fh;
+ struct device_d dev;
+ void *priv;
+ void __iomem *regs;
+ void __iomem *regs_data;
+ int programmed;
+};
+
+
+#endif /*_LINUX_FPGA_MGR_H */
diff --git a/include/libbb.h b/include/libbb.h
index a3a13b41ce..e191874052 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -8,8 +8,10 @@
char *concat_path_file(const char *path, const char *filename);
char *concat_subpath_file(const char *path, const char *f);
-int execable_file(const char *name);
+bool execable_file(const char *name);
char *find_execable(const char *filename);
+char *find_path(const char *path, const char *filename,
+ bool (*filter)(const char *));
char* last_char_is(const char *s, int c);
enum {
diff --git a/include/libfile.h b/include/libfile.h
index 350ddddf70..3c2fe1714d 100644
--- a/include/libfile.h
+++ b/include/libfile.h
@@ -5,6 +5,7 @@
int pwrite_full(int fd, const void *buf, size_t size, loff_t offset);
int write_full(int fd, const void *buf, size_t size);
int read_full(int fd, void *buf, size_t size);
+int copy_fd(int in, int out);
char *read_file_line(const char *fmt, ...);
diff --git a/include/linux/reset.h b/include/linux/reset.h
index a166fe1cfe..726cb5c205 100644
--- a/include/linux/reset.h
+++ b/include/linux/reset.h
@@ -11,6 +11,8 @@ int reset_control_assert(struct reset_control *rstc);
int reset_control_deassert(struct reset_control *rstc);
struct reset_control *reset_control_get(struct device_d *dev, const char *id);
+struct reset_control *of_reset_control_get(struct device_node *node,
+ const char *id);
void reset_control_put(struct reset_control *rstc);
int __must_check device_reset(struct device_d *dev);
diff --git a/include/of.h b/include/of.h
index 880e380359..d82790c052 100644
--- a/include/of.h
+++ b/include/of.h
@@ -110,8 +110,8 @@ void of_print_properties(struct device_node *node);
void of_diff(struct device_node *a, struct device_node *b, int indent);
int of_probe(void);
int of_parse_dtb(struct fdt_header *fdt);
-struct device_node *of_unflatten_dtb(const void *fdt);
-struct device_node *of_unflatten_dtb_const(const void *infdt);
+struct device_node *of_unflatten_dtb(const void *fdt, int size);
+struct device_node *of_unflatten_dtb_const(const void *infdt, int size);
struct cdev;
@@ -163,6 +163,7 @@ extern struct device_node *of_create_node(struct device_node *root,
const char *path);
extern struct device_node *of_copy_node(struct device_node *parent,
const struct device_node *other);
+extern struct device_node *of_dup(const struct device_node *root);
extern void of_delete_node(struct device_node *node);
extern const char *of_get_machine_compatible(void);
@@ -1059,11 +1060,20 @@ static inline struct device_node *of_find_root_node(struct device_node *node)
return node;
}
+struct of_overlay_filter {
+ bool (*filter_filename)(struct of_overlay_filter *, const char *filename);
+ bool (*filter_content)(struct of_overlay_filter *, struct device_node *);
+ char *name;
+ struct list_head list;
+};
+
#ifdef CONFIG_OF_OVERLAY
struct device_node *of_resolve_phandles(struct device_node *root,
const struct device_node *overlay);
int of_overlay_apply_tree(struct device_node *root,
struct device_node *overlay);
+int of_overlay_apply_file(struct device_node *root, const char *filename,
+ bool filter);
int of_register_overlay(struct device_node *overlay);
int of_process_overlay(struct device_node *root,
struct device_node *overlay,
@@ -1071,7 +1081,11 @@ int of_process_overlay(struct device_node *root,
struct device_node *overlay, void *data),
void *data);
-int of_firmware_load_overlay(struct device_node *overlay, const char *path);
+int of_overlay_pre_load_firmware(struct device_node *root, struct device_node *overlay);
+int of_overlay_load_firmware(void);
+void of_overlay_load_firmware_clear(void);
+void of_overlay_set_basedir(const char *path);
+int of_overlay_register_filter(struct of_overlay_filter *);
#else
static inline struct device_node *of_resolve_phandles(struct device_node *root,
const struct device_node *overlay)
@@ -1085,6 +1099,12 @@ static inline int of_overlay_apply_tree(struct device_node *root,
return -ENOSYS;
}
+static inline int of_overlay_apply_file(struct device_node *root,
+ const char *filename, bool filter)
+{
+ return -ENOSYS;
+}
+
static inline int of_register_overlay(struct device_node *overlay)
{
return -ENOSYS;
@@ -1099,10 +1119,25 @@ static inline int of_process_overlay(struct device_node *root,
return -ENOSYS;
}
-static inline int of_firmware_load_overlay(struct device_node *overlay, const char *path)
+static inline int of_overlay_pre_load_firmware(struct device_node *root,
+ struct device_node *overlay)
{
return -ENOSYS;
}
+
+static inline int of_overlay_load_firmware(void)
+{
+ return 0;
+}
+
+static inline void of_overlay_load_firmware_clear(void)
+{
+}
+
+static inline void of_overlay_set_basedir(const char *path)
+{
+}
+
#endif
#endif /* __OF_H */
diff --git a/lib/libbb.c b/lib/libbb.c
index d0c9bf4d80..642e54d78f 100644
--- a/lib/libbb.c
+++ b/lib/libbb.c
@@ -49,11 +49,47 @@ char *concat_subpath_file(const char *path, const char *f)
}
EXPORT_SYMBOL(concat_subpath_file);
+/**
+ * find_path - find a file in a colon separated path
+ * @path: The search path, colon separated
+ * @filename: The filename to search for
+ * @filter: filter function
+ *
+ * searches for @filename in a colon separated list of directories given in
+ * @path. @filter should return true when the current file matches the expectations,
+ * false otherwise. @filter should check for existence of the file, but could also
+ * check for additional flags.
+ */
+char *find_path(const char *path, const char *filename,
+ bool (*filter)(const char *))
+{
+ char *p, *n, *freep;
+
+ freep = p = strdup(path);
+ while (p) {
+ n = strchr(p, ':');
+ if (n)
+ *n++ = '\0';
+ if (*p != '\0') { /* it's not a PATH="foo::bar" situation */
+ p = concat_path_file(p, filename);
+ if (filter(p)) {
+ free(freep);
+ return p;
+ }
+ free(p);
+ }
+ p = n;
+ }
+ free(freep);
+ return NULL;
+}
+EXPORT_SYMBOL(find_path);
+
/* check if path points to an executable file;
* return 1 if found;
* return 0 otherwise;
*/
-int execable_file(const char *name)
+bool execable_file(const char *name)
{
struct stat s;
return (!stat(name, &s) && S_ISREG(s.st_mode));
@@ -67,25 +103,7 @@ EXPORT_SYMBOL(execable_file);
*/
char *find_execable(const char *filename)
{
- char *path, *p, *n;
-
- p = path = strdup(getenv("PATH"));
- while (p) {
- n = strchr(p, ':');
- if (n)
- *n++ = '\0';
- if (*p != '\0') { /* it's not a PATH="foo::bar" situation */
- p = concat_path_file(p, filename);
- if (execable_file(p)) {
- free(path);
- return p;
- }
- free(p);
- }
- p = n;
- }
- free(path);
- return NULL;
+ return find_path(getenv("PATH"), filename, execable_file);
}
EXPORT_SYMBOL(find_execable);
diff --git a/lib/libfile.c b/lib/libfile.c
index 4ab8db11ad..40b1d8bb27 100644
--- a/lib/libfile.c
+++ b/lib/libfile.c
@@ -100,6 +100,29 @@ int read_full(int fd, void *buf, size_t size)
}
EXPORT_SYMBOL(read_full);
+int copy_fd(int in, int out)
+{
+ int bs = 4096, ret;
+ void *buf = malloc(bs);
+
+ if (!buf)
+ return -ENOMEM;
+
+ while (1) {
+ ret = read(in, buf, bs);
+ if (ret <= 0)
+ break;
+
+ ret = write_full(out, buf, ret);
+ if (ret < 0)
+ break;
+ }
+
+ free(buf);
+
+ return ret;
+}
+
/*
* read_file_line - read a line from a file
*