summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Documentation/user/devicetree.rst45
-rw-r--r--Makefile4
-rw-r--r--arch/arm/boards/embest-marsboard/board.c44
-rw-r--r--arch/arm/boards/freescale-mx6-sabrelite/board.c58
-rw-r--r--arch/arm/boards/phytec-som-imx6/board.c159
-rw-r--r--arch/arm/boards/protonic-imx6/board.c6
-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/cpu/mmu.c28
-rw-r--r--arch/arm/dts/imx6q-marsboard.dts5
-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.c8
-rw-r--r--arch/arm/mach-imx/esdctl.c12
-rw-r--r--arch/arm/mach-imx/include/mach/devices-imx53.h27
-rw-r--r--arch/arm/mach-socfpga/include/mach/cyclone5-regs.h1
-rw-r--r--arch/arm/mach-stm32mp/ddrctrl.c12
-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.c8
-rw-r--r--arch/sandbox/Makefile7
-rw-r--r--arch/sandbox/board/Makefile2
-rw-r--r--arch/sandbox/board/env/init/state13
-rw-r--r--arch/sandbox/board/hostfile.c21
-rw-r--r--arch/sandbox/board/power.c33
-rw-r--r--arch/sandbox/board/stickypage.S26
-rw-r--r--arch/sandbox/board/watchdog.c20
-rw-r--r--arch/sandbox/configs/sandbox_defconfig2
-rw-r--r--arch/sandbox/dts/sandbox.dts36
-rw-r--r--arch/sandbox/mach-sandbox/include/mach/linux.h1
-rw-r--r--arch/sandbox/os/common.c83
-rw-r--r--commands/Kconfig7
-rw-r--r--commands/Makefile1
-rw-r--r--commands/hwclock.c8
-rw-r--r--commands/md.c2
-rw-r--r--commands/nvmem.c24
-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/Kconfig4
-rw-r--r--common/Makefile1
-rw-r--r--common/bbu.c27
-rw-r--r--common/blspec.c99
-rw-r--r--common/bootm.c4
-rw-r--r--common/console.c20
-rw-r--r--common/deep-probe.c39
-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/base/driver.c28
-rw-r--r--drivers/clk/clk.c7
-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/hab/habv4.c3
-rw-r--r--drivers/i2c/i2c.c8
-rw-r--r--drivers/mtd/nand/nand_base.c1
-rw-r--r--drivers/nvmem/Kconfig6
-rw-r--r--drivers/nvmem/Makefile4
-rw-r--r--drivers/nvmem/bsec.c1
-rw-r--r--drivers/nvmem/core.c27
-rw-r--r--drivers/nvmem/partition.c40
-rw-r--r--drivers/nvmem/rmem.c67
-rw-r--r--drivers/of/Kconfig1
-rw-r--r--drivers/of/base.c114
-rw-r--r--drivers/of/fdt.c17
-rw-r--r--drivers/of/of_firmware.c137
-rw-r--r--drivers/of/of_net.c78
-rw-r--r--drivers/of/overlay.c355
-rw-r--r--drivers/of/partition.c7
-rw-r--r--drivers/of/platform.c240
-rw-r--r--drivers/phy/phy-core.c4
-rw-r--r--drivers/pinctrl/pinctrl-stm32.c5
-rw-r--r--drivers/pinctrl/pinctrl.c16
-rw-r--r--drivers/power/reset/Kconfig10
-rw-r--r--drivers/power/reset/Makefile1
-rw-r--r--drivers/power/reset/nvmem-reboot-mode.c83
-rw-r--r--drivers/regulator/core.c6
-rw-r--r--drivers/regulator/fixed.c12
-rw-r--r--drivers/reset/core.c8
-rw-r--r--drivers/reset/reset-stm32.c4
-rw-r--r--drivers/spi/Kconfig2
-rw-r--r--drivers/spi/spi.c2
-rw-r--r--drivers/usb/misc/usb251xb.c2
-rw-r--r--drivers/w1/masters/w1-gpio.c15
-rw-r--r--drivers/w1/slaves/w1_ds2431.c10
-rw-r--r--drivers/w1/slaves/w1_ds2433.c12
-rw-r--r--dts/Bindings/connector/usb-connector.yaml15
-rw-r--r--dts/Bindings/hwmon/ti,ads7828.yaml2
-rw-r--r--dts/Bindings/media/renesas,drif.yaml4
-rw-r--r--dts/Bindings/sound/amlogic,gx-sound-card.yaml4
-rw-r--r--dts/include/dt-bindings/usb/pd.h89
-rw-r--r--dts/src/riscv/sifive/fu740-c000.dtsi2
-rw-r--r--fs/devfs-core.c2
-rw-r--r--fs/fs.c16
-rw-r--r--include/asm-generic/barebox.lds.h11
-rw-r--r--include/deep-probe.h26
-rw-r--r--include/driver.h3
-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/init.h10
-rw-r--r--include/libbb.h4
-rw-r--r--include/libfile.h1
-rw-r--r--include/linux/nvmem-consumer.h2
-rw-r--r--include/linux/nvmem-provider.h8
-rw-r--r--include/linux/reset.h2
-rw-r--r--include/linux/string.h4
-rw-r--r--include/of.h85
-rw-r--r--include/of_net.h21
-rw-r--r--lib/Kconfig2
-rw-r--r--lib/image-sparse.c4
-rw-r--r--lib/libbb.c58
-rw-r--r--lib/libfile.c23
-rw-r--r--lib/string.c18
-rw-r--r--net/eth.c24
-rw-r--r--test/kconfig/base.cfg1
-rw-r--r--test/kconfig/full.cfg1
-rw-r--r--test/self/Kconfig4
-rw-r--r--test/self/Makefile1
-rw-r--r--test/self/progress-notifier.c79
140 files changed, 3283 insertions, 690 deletions
diff --git a/.gitignore b/.gitignore
index d7a37b3c9b..529bcfc212 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,6 +42,7 @@ Module.symvers
/TAGS
/barebox*
/System.map
+/stickypage.bin
#
# git files that we don't want to ignore even it they are dot-files
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/Makefile b/Makefile
index f3a1bcc04c..47ead62422 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
VERSION = 2021
-PATCHLEVEL = 06
+PATCHLEVEL = 07
SUBLEVEL = 0
EXTRAVERSION =
NAME = None
@@ -1123,7 +1123,7 @@ endif # CONFIG_MODULES
# Directories & files removed with 'make clean'
CLEAN_DIRS += $(MODVERDIR)
-CLEAN_FILES += barebox System.map include/generated/barebox_default_env.h \
+CLEAN_FILES += barebox System.map stickypage.bin include/generated/barebox_default_env.h \
.tmp_version .tmp_barebox* barebox.bin barebox.map \
.tmp_kallsyms* barebox.ldr compile_commands.json \
scripts/bareboxenv-target barebox-flash-image \
diff --git a/arch/arm/boards/embest-marsboard/board.c b/arch/arm/boards/embest-marsboard/board.c
index 66893434c2..a11b7b6579 100644
--- a/arch/arm/boards/embest-marsboard/board.c
+++ b/arch/arm/boards/embest-marsboard/board.c
@@ -10,6 +10,7 @@
#include <envfs.h>
#include <mach/bbu.h>
#include <linux/phy.h>
+#include <deep-probe.h>
static int ar8035_phy_fixup(struct phy_device *dev)
{
@@ -18,37 +19,22 @@ static int ar8035_phy_fixup(struct phy_device *dev)
/* Ar803x phy SmartEEE feature cause link status generates glitch,
* which cause ethernet link down/up issue, so disable SmartEEE
*/
- phy_write(dev, 0xd, 0x3);
- phy_write(dev, 0xe, 0x805d);
- phy_write(dev, 0xd, 0x4003);
+ val = phy_read_mmd_indirect(dev, 0x805d, 0x3);
+ phy_write(dev, MII_MMD_DATA, val & ~(1 << 8));
- val = phy_read(dev, 0xe);
- phy_write(dev, 0xe, val & ~(1 << 8));
+ val = phy_read_mmd_indirect(dev, 0x4003, 0x3);
+ phy_write(dev, MII_MMD_DATA, val & ~(1 << 8));
- /* To enable AR8031 ouput a 125MHz clk from CLK_25M */
- phy_write(dev, 0xd, 0x7);
- phy_write(dev, 0xe, 0x8016);
- phy_write(dev, 0xd, 0x4007);
-
- val = phy_read(dev, 0xe);
+ val = phy_read_mmd_indirect(dev, 0x4007, 0x3);
val &= 0xffe3;
val |= 0x18;
- phy_write(dev, 0xe, val);
-
- /* introduce tx clock delay */
- phy_write(dev, 0x1d, 0x5);
- val = phy_read(dev, 0x1e);
- val |= 0x0100;
- phy_write(dev, 0x1e, val);
+ phy_write(dev, MII_MMD_DATA, val);
return 0;
}
-static int marsboard_device_init(void)
+static int marsboard_device_init(struct device_d *dev)
{
- if (!of_machine_is_compatible("embest,imx6q-marsboard"))
- return 0;
-
barebox_set_hostname("marsboard");
phy_register_fixup_for_uid(0x004dd072, 0xffffffef, ar8035_phy_fixup);
@@ -60,4 +46,16 @@ static int marsboard_device_init(void)
return 0;
}
-device_initcall(marsboard_device_init);
+
+static const struct of_device_id marsboard_of_match[] = {
+ { .compatible = "embest,imx6q-marsboard" },
+ { /* sentinel */ },
+};
+BAREBOX_DEEP_PROBE_ENABLE(marsboard_of_match);
+
+static struct driver_d marsboard_driver = {
+ .name = "board-mars",
+ .probe = marsboard_device_init,
+ .of_compatible = marsboard_of_match,
+};
+postcore_platform_driver(marsboard_driver);
diff --git a/arch/arm/boards/freescale-mx6-sabrelite/board.c b/arch/arm/boards/freescale-mx6-sabrelite/board.c
index 1b39ef82c6..f6eac4c0f3 100644
--- a/arch/arm/boards/freescale-mx6-sabrelite/board.c
+++ b/arch/arm/boards/freescale-mx6-sabrelite/board.c
@@ -13,7 +13,9 @@
#include <mach/bbu.h>
#include <asm/armlinux.h>
#include <generated/mach-types.h>
+#include <of.h>
#include <partition.h>
+#include <deep-probe.h>
#include <linux/phy.h>
#include <asm/io.h>
#include <asm/mmu.h>
@@ -98,10 +100,6 @@ static int sabrelite_ksz9021rn_setup(void)
{
int ret;
- if (!of_machine_is_compatible("fsl,imx6q-sabrelite") &&
- !of_machine_is_compatible("fsl,imx6dl-sabrelite"))
- return 0;
-
mxc_iomux_v3_setup_multiple_pads(sabrelite_enet_gpio_pads,
ARRAY_SIZE(sabrelite_enet_gpio_pads));
@@ -118,11 +116,6 @@ static int sabrelite_ksz9021rn_setup(void)
return 0;
}
-/*
- * Do this before the fec initializes but after our
- * gpios are available.
- */
-fs_initcall(sabrelite_ksz9021rn_setup);
static void sabrelite_ehci_init(void)
{
@@ -132,11 +125,22 @@ static void sabrelite_ehci_init(void)
gpio_set_value(IMX_GPIO_NR(7, 12), 1);
}
-static int sabrelite_devices_init(void)
+static int sabrelite_probe(struct device_d *dev)
{
- if (!of_machine_is_compatible("fsl,imx6q-sabrelite") &&
- !of_machine_is_compatible("fsl,imx6dl-sabrelite"))
- return 0;
+ int ret;
+
+ phy_register_fixup_for_uid(PHY_ID_KSZ9021, MICREL_PHY_ID_MASK,
+ ksz9021rn_phy_fixup);
+
+ barebox_set_hostname("sabrelite");
+
+ ret = of_devices_ensure_probed_by_property("gpio-controller");
+ if (ret)
+ return ret;
+
+ ret = sabrelite_ksz9021rn_setup();
+ if (ret)
+ return ret;
sabrelite_ehci_init();
@@ -147,19 +151,21 @@ static int sabrelite_devices_init(void)
return 0;
}
-device_initcall(sabrelite_devices_init);
-
-static int sabrelite_coredevices_init(void)
-{
- if (!of_machine_is_compatible("fsl,imx6q-sabrelite") &&
- !of_machine_is_compatible("fsl,imx6dl-sabrelite"))
- return 0;
+static const struct of_device_id sabrelite_match[] = {
+ {
+ .compatible = "fsl,imx6q-sabrelite",
+ }, {
+ .compatible = "fsl,imx6dl-sabrelite",
+ },
+ { /* Sentinel */ },
+};
- phy_register_fixup_for_uid(PHY_ID_KSZ9021, MICREL_PHY_ID_MASK,
- ksz9021rn_phy_fixup);
+static struct driver_d sabrelite_driver = {
+ .name = "physom-imx6",
+ .probe = sabrelite_probe,
+ .of_compatible = sabrelite_match,
+};
- barebox_set_hostname("sabrelite");
+postcore_platform_driver(sabrelite_driver);
- return 0;
-}
-coredevice_initcall(sabrelite_coredevices_init);
+BAREBOX_DEEP_PROBE_ENABLE(sabrelite_match);
diff --git a/arch/arm/boards/phytec-som-imx6/board.c b/arch/arm/boards/phytec-som-imx6/board.c
index bac3e8a8a1..1e515a093a 100644
--- a/arch/arm/boards/phytec-som-imx6/board.c
+++ b/arch/arm/boards/phytec-som-imx6/board.c
@@ -14,6 +14,7 @@
#include <gpio.h>
#include <init.h>
#include <of.h>
+#include <deep-probe.h>
#include <i2c/i2c.h>
#include <mach/bbu.h>
#include <platform_data/eth-fec.h>
@@ -110,6 +111,10 @@ static int phycore_da9062_setup_buck_mode(void)
if (!pmic_np)
return -ENODEV;
+ ret = of_device_ensure_probed(pmic_np);
+ if (ret)
+ return ret;
+
adapter = of_find_i2c_adapter_by_node(pmic_np->parent);
if (!adapter)
return -ENODEV;
@@ -141,15 +146,29 @@ err_out:
return ret;
}
-static int physom_imx6_devices_init(void)
+#define IS_PHYFLEX BIT(0)
+#define IS_PHYCORE BIT(1)
+#define IS_PHYCARD BIT(2)
+#define IS_PHYCORE_UL BIT(3)
+#define HAS_MMC3 BIT(4)
+#define HAS_MMC1 BIT(5)
+
+struct board_data {
+ unsigned flags;
+};
+
+static int physom_imx6_probe(struct device_d *dev)
{
int ret;
char *environment_path, *default_environment_path;
char *envdev, *default_envdev;
+ const struct board_data *brd = device_get_match_data(dev);
+ unsigned flags = brd->flags;
- if (of_machine_is_compatible("phytec,imx6q-pfla02")
- || of_machine_is_compatible("phytec,imx6dl-pfla02")
- || of_machine_is_compatible("phytec,imx6s-pfla02")) {
+ if (flags & IS_PHYFLEX) {
+ ret = of_devices_ensure_probed_by_property("gpio-controller");
+ if (ret)
+ return ret;
phyflex_err006282_workaround();
@@ -164,21 +183,14 @@ static int physom_imx6_devices_init(void)
imx6_bbu_internal_mmc_register_handler("mmc2",
"/dev/mmc2", 0);
- } else if (of_machine_is_compatible("phytec,imx6q-pcaaxl3")) {
-
+ } else if (flags & IS_PHYCARD) {
barebox_set_hostname("phyCARD-i.MX6");
default_environment_path = "/chosen/environment-nand";
default_envdev = "NAND flash";
imx6_bbu_internal_mmc_register_handler("mmc2",
"/dev/mmc2", 0);
-
- } else if (of_machine_is_compatible("phytec,imx6q-pcm058-nand")
- || of_machine_is_compatible("phytec,imx6q-pcm058-emmc")
- || of_machine_is_compatible("phytec,imx6dl-pcm058-nand")
- || of_machine_is_compatible("phytec,imx6qp-pcm058-nand")
- || of_machine_is_compatible("phytec,imx6dl-pcm058-emmc")) {
-
+ } else if (flags & IS_PHYCORE) {
if (phycore_da9062_setup_buck_mode())
pr_err("Setting PMIC BUCK mode failed\n");
@@ -189,8 +201,7 @@ static int physom_imx6_devices_init(void)
imx6_bbu_internal_mmc_register_handler("mmc0",
"/dev/mmc0", 0);
- } else if (of_machine_is_compatible("phytec,imx6ul-pcl063-nand")
- || of_machine_is_compatible("phytec,imx6ul-pcl063-emmc")) {
+ } else if (flags & IS_PHYCORE_UL) {
barebox_set_hostname("phyCORE-i.MX6UL");
default_environment_path = "/chosen/environment-nand";
default_envdev = "NAND flash";
@@ -201,8 +212,9 @@ static int physom_imx6_devices_init(void)
imx6_bbu_internal_mmc_register_handler("mmc0",
"/dev/mmc0", 0);
- } else
- return 0;
+ } else {
+ return -EINVAL;
+ }
switch (bootsource_get()) {
case BOOTSOURCE_MMC:
@@ -234,14 +246,13 @@ static int physom_imx6_devices_init(void)
pr_notice("Using environment in %s\n", envdev);
- if (of_machine_is_compatible("phytec,imx6q-pcm058-emmc")
- || of_machine_is_compatible("phytec,imx6dl-pcm058-emmc")) {
+ if (flags & HAS_MMC3) {
imx6_bbu_internal_mmc_register_handler("mmc3",
"/dev/mmc3",
BBU_HANDLER_FLAG_DEFAULT);
imx6_bbu_internal_mmcboot_register_handler("mmc3-boot",
"mmc3", 0);
- } else if (of_machine_is_compatible("phytec,imx6ul-pcl063-emmc")) {
+ } else if (flags & HAS_MMC1) {
imx6_bbu_internal_mmc_register_handler("mmc1",
"/dev/mmc1",
BBU_HANDLER_FLAG_DEFAULT);
@@ -254,23 +265,107 @@ static int physom_imx6_devices_init(void)
defaultenv_append_directory(defaultenv_physom_imx6);
/* Overwrite file /env/init/automount */
- if (of_machine_is_compatible("phytec,imx6q-pfla02")
- || of_machine_is_compatible("phytec,imx6dl-pfla02")
- || of_machine_is_compatible("phytec,imx6s-pfla02")
- || of_machine_is_compatible("phytec,imx6q-pcaaxl3")) {
+ if (flags & IS_PHYCARD || flags & IS_PHYFLEX) {
defaultenv_append_directory(defaultenv_physom_imx6);
- } else if (of_machine_is_compatible("phytec,imx6qp-pcm058-nand")
- || of_machine_is_compatible("phytec,imx6q-pcm058-nand")
- || of_machine_is_compatible("phytec,imx6q-pcm058-emmc")
- || of_machine_is_compatible("phytec,imx6dl-pcm058-nand")
- || of_machine_is_compatible("phytec,imx6dl-pcm058-emmc")) {
+ } else if (flags & IS_PHYCORE) {
defaultenv_append_directory(defaultenv_physom_imx6);
defaultenv_append_directory(defaultenv_physom_imx6_phycore);
- } else if (of_machine_is_compatible("phytec,imx6ul-pcl063-nand")
- || of_machine_is_compatible("phytec,imx6ul-pcl063-emmc")) {
+ } else if (flags & IS_PHYCORE_UL) {
defaultenv_append_directory(defaultenv_physom_imx6ul_phycore);
}
return 0;
}
-device_initcall(physom_imx6_devices_init);
+
+static struct board_data imx6q_pfla02 = {
+ .flags = IS_PHYFLEX,
+};
+
+static struct board_data imx6dl_pfla02 = {
+ .flags = IS_PHYFLEX,
+};
+
+static struct board_data imx6s_pfla02 = {
+ .flags = IS_PHYFLEX,
+};
+
+static struct board_data imx6q_pcaaxl3 = {
+ .flags = IS_PHYCARD,
+};
+
+static struct board_data imx6q_pcm058_nand = {
+ .flags = IS_PHYCORE,
+};
+
+static struct board_data imx6q_pcm058_emmc = {
+ .flags = IS_PHYCORE | HAS_MMC3,
+};
+
+static struct board_data imx6dl_pcm058_nand = {
+ .flags = IS_PHYCORE,
+};
+
+static struct board_data imx6qp_pcm058_nand = {
+ .flags = IS_PHYCORE,
+};
+
+static struct board_data imx6dl_pcm058_emmc = {
+ .flags = IS_PHYCORE | HAS_MMC3,
+};
+
+static struct board_data imx6ul_pcl063_nand = {
+ .flags = IS_PHYCORE_UL,
+};
+
+static struct board_data imx6ul_pcl063_emmc = {
+ .flags = IS_PHYCORE_UL | HAS_MMC1,
+};
+
+
+static const struct of_device_id physom_imx6_match[] = {
+ {
+ .compatible = "phytec,imx6q-pfla02",
+ .data = &imx6q_pfla02,
+ }, {
+ .compatible = "phytec,imx6dl-pfla02",
+ .data = &imx6dl_pfla02,
+ }, {
+ .compatible = "phytec,imx6s-pfla02",
+ .data = &imx6s_pfla02,
+ }, {
+ .compatible = "phytec,imx6q-pcaaxl3",
+ .data = &imx6q_pcaaxl3,
+ }, {
+ .compatible = "phytec,imx6q-pcm058-nand",
+ .data = &imx6q_pcm058_nand,
+ }, {
+ .compatible = "phytec,imx6q-pcm058-emmc",
+ .data = &imx6q_pcm058_emmc,
+ }, {
+ .compatible = "phytec,imx6dl-pcm058-nand",
+ .data = &imx6dl_pcm058_nand,
+ }, {
+ .compatible = "phytec,imx6qp-pcm058-nand",
+ .data = &imx6qp_pcm058_nand,
+ }, {
+ .compatible = "phytec,imx6dl-pcm058-emmc",
+ .data = &imx6dl_pcm058_emmc,
+ }, {
+ .compatible = "phytec,imx6ul-pcl063-nand",
+ .data = &imx6ul_pcl063_nand,
+ }, {
+ .compatible = "phytec,imx6ul-pcl063-emmc",
+ .data = &imx6ul_pcl063_emmc,
+ },
+ { /* Sentinel */ },
+};
+
+static struct driver_d physom_imx6_driver = {
+ .name = "physom-imx6",
+ .probe = physom_imx6_probe,
+ .of_compatible = physom_imx6_match,
+};
+
+postcore_platform_driver(physom_imx6_driver);
+
+BAREBOX_DEEP_PROBE_ENABLE(physom_imx6_match);
diff --git a/arch/arm/boards/protonic-imx6/board.c b/arch/arm/boards/protonic-imx6/board.c
index a718d54df4..6eb31a4bc4 100644
--- a/arch/arm/boards/protonic-imx6/board.c
+++ b/arch/arm/boards/protonic-imx6/board.c
@@ -794,12 +794,6 @@ static int prt_imx6_probe(struct device_d *dev)
if (ret)
goto free_priv;
- ret = of_register_fixup(prt_imx6_of_fixup, priv);
- if (ret) {
- dev_err(dev, "Failed to register fixup\n");
- goto free_priv;
- }
-
prt_priv = priv;
return 0;
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/cpu/mmu.c b/arch/arm/cpu/mmu.c
index 6af228505d..6388e1bf14 100644
--- a/arch/arm/cpu/mmu.c
+++ b/arch/arm/cpu/mmu.c
@@ -138,6 +138,29 @@ static u32 *arm_create_pte(unsigned long virt, uint32_t flags)
return table;
}
+static u32 pmd_flags_to_pte(u32 pmd)
+{
+ u32 pte = 0;
+
+ if (pmd & PMD_SECT_BUFFERABLE)
+ pte |= PTE_BUFFERABLE;
+ if (pmd & PMD_SECT_CACHEABLE)
+ pte |= PTE_CACHEABLE;
+ if (pmd & PMD_SECT_nG)
+ pte |= PTE_EXT_NG;
+ if (pmd & PMD_SECT_XN)
+ pte |= PTE_EXT_XN;
+
+ /* TEX[2:0] */
+ pte |= PTE_EXT_TEX((pmd >> 12) & 7);
+ /* AP[1:0] */
+ pte |= ((pmd >> 10) & 0x3) << 4;
+ /* AP[2] */
+ pte |= ((pmd >> 15) & 0x1) << 9;
+
+ return pte;
+}
+
int arch_remap_range(void *start, size_t size, unsigned flags)
{
u32 addr = (u32)start;
@@ -206,11 +229,8 @@ int arch_remap_range(void *start, size_t size, unsigned flags)
* If PTE is not found it means that
* we needs to split this section and
* create a new page table for it
- *
- * NOTE: Here we assume that section
- * we just split was mapped as cached
*/
- table = arm_create_pte(addr, pte_flags_cached);
+ table = arm_create_pte(addr, pmd_flags_to_pte(*pgd));
pte = find_pte(addr);
BUG_ON(!pte);
}
diff --git a/arch/arm/dts/imx6q-marsboard.dts b/arch/arm/dts/imx6q-marsboard.dts
index 1d9f8f005d..cc5edfff66 100644
--- a/arch/arm/dts/imx6q-marsboard.dts
+++ b/arch/arm/dts/imx6q-marsboard.dts
@@ -36,6 +36,11 @@
&fec {
phy-reset-duration = <2>;
+ phy-mode = "rgmii-id";
+};
+
+&rgmii_phy {
+ qca,clk-out-frequency = <125000000>;
};
&ocotp {
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 0ba4d30b8e..8382ffdf1b 100644
--- a/arch/arm/lib64/armlinux.c
+++ b/arch/arm/lib64/armlinux.c
@@ -11,11 +11,19 @@ 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))
return PTR_ERR(fn);
+ 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-imx/esdctl.c b/arch/arm/mach-imx/esdctl.c
index 5355f55c6f..64a5832494 100644
--- a/arch/arm/mach-imx/esdctl.c
+++ b/arch/arm/mach-imx/esdctl.c
@@ -696,7 +696,17 @@ static struct driver_d imx_esdctl_driver = {
.of_compatible = DRV_OF_COMPAT(imx_esdctl_dt_ids),
};
-mem_platform_driver(imx_esdctl_driver);
+static int imx_esdctl_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&imx_esdctl_driver);
+ if (ret)
+ return ret;
+
+ return of_devices_ensure_probed_by_dev_id(imx_esdctl_dt_ids);
+}
+mem_initcall(imx_esdctl_init);
/*
* The i.MX SoCs usually have two SDRAM chipselects. The following
diff --git a/arch/arm/mach-imx/include/mach/devices-imx53.h b/arch/arm/mach-imx/include/mach/devices-imx53.h
index e5c257a40b..3d1e8a27a8 100644
--- a/arch/arm/mach-imx/include/mach/devices-imx53.h
+++ b/arch/arm/mach-imx/include/mach/devices-imx53.h
@@ -77,33 +77,6 @@ static inline struct device_d *imx53_add_mmc3(struct esdhc_platform_data *pdata)
return imx_add_esdhc_imx5((void *)MX53_ESDHC4_BASE_ADDR, 3, pdata);
}
-static inline struct device_d *imx53_add_nand(struct imx_nand_platform_data *pdata)
-{
- struct resource res[] = {
- {
- .start = MX53_NFC_BASE_ADDR,
- .end = MX53_NFC_BASE_ADDR + SZ_4K - 1,
- .flags = IORESOURCE_MEM,
- }, {
- .start = MX53_NFC_AXI_BASE_ADDR,
- .end = MX53_NFC_AXI_BASE_ADDR + SZ_4K - 1,
- .flags = IORESOURCE_MEM,
- },
- };
- struct device_d *dev = xzalloc(sizeof(*dev));
-
- dev->resource = xzalloc(sizeof(struct resource) * ARRAY_SIZE(res));
- memcpy(dev->resource, res, sizeof(struct resource) * ARRAY_SIZE(res));
- dev->num_resources = ARRAY_SIZE(res);
- dev_set_name(dev, "imx_nand");
- dev->id = DEVICE_ID_DYNAMIC;
- dev->platform_data = pdata;
-
- platform_device_register(dev);
-
- return dev;
-}
-
static inline struct device_d *imx53_add_kpp(struct matrix_keymap_data *pdata)
{
return imx_add_kpp((void *)MX53_KPP_BASE_ADDR, pdata);
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/arm/mach-stm32mp/ddrctrl.c b/arch/arm/mach-stm32mp/ddrctrl.c
index 43486c57c5..7f2013c22d 100644
--- a/arch/arm/mach-stm32mp/ddrctrl.c
+++ b/arch/arm/mach-stm32mp/ddrctrl.c
@@ -146,4 +146,14 @@ static struct driver_d stm32mp1_ddr_driver = {
.of_compatible = DRV_OF_COMPAT(stm32mp1_ddr_dt_ids),
};
-mem_platform_driver(stm32mp1_ddr_driver);
+static int stm32mp1_ddr_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&stm32mp1_ddr_driver);
+ if (ret)
+ return ret;
+
+ return of_devices_ensure_probed_by_dev_id(stm32mp1_ddr_dt_ids);
+}
+mem_initcall(stm32mp1_ddr_init);
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 835ff345e3..6984f282be 100644
--- a/arch/riscv/lib/bootm.c
+++ b/arch/riscv/lib/bootm.c
@@ -10,11 +10,19 @@ static int do_bootm_linux(struct image_data *data)
void (*fn)(unsigned long a0, unsigned long dtb, unsigned long a2);
phys_addr_t devicetree;
long hartid = riscv_hartid();
+ int ret;
fn = booti_load_image(data, &devicetree);
if (IS_ERR(fn))
return PTR_ERR(fn);
+ if (data->dryrun)
+ return 0;
+
+ ret = of_overlay_load_firmware();
+ if (ret)
+ return ret;
+
shutdown_barebox();
fn(hartid >= 0 ? hartid : 0, devicetree, 0);
diff --git a/arch/sandbox/Makefile b/arch/sandbox/Makefile
index 5fc7e227be..ba2614ea5f 100644
--- a/arch/sandbox/Makefile
+++ b/arch/sandbox/Makefile
@@ -75,3 +75,10 @@ common-y += $(BOARD) arch/sandbox/os/ arch/sandbox/lib/
common-$(CONFIG_OFTREE) += arch/sandbox/dts/
CLEAN_FILES += $(BOARD)/barebox.lds
+
+OBJCOPYFLAGS_stickypage.bin = -O binary
+
+stickypage.bin: arch/sandbox/board/stickypage.o
+ $(call if_changed,objcopy)
+
+all: stickypage.bin
diff --git a/arch/sandbox/board/Makefile b/arch/sandbox/board/Makefile
index ffb1dbc21e..59fece60ef 100644
--- a/arch/sandbox/board/Makefile
+++ b/arch/sandbox/board/Makefile
@@ -10,3 +10,5 @@ obj-y += watchdog.o
obj-$(CONFIG_LED) += led.o
extra-y += barebox.lds
+
+extra-y += stickypage.o
diff --git a/arch/sandbox/board/env/init/state b/arch/sandbox/board/env/init/state
deleted file mode 100644
index b8a2b42a53..0000000000
--- a/arch/sandbox/board/env/init/state
+++ /dev/null
@@ -1,13 +0,0 @@
-if [ "x$state.dirty" != "x1" -o $global.system.reset != "POR" ]; then
- exit
-fi
-
-source /env/data/ansi-colors
-
-echo -e $CYAN
-echo "*******************************************************"
-echo "*** Inconsistent barebox state buckets detected ***"
-echo "*** This is normal for a first boot ***"
-echo "*** barebox will repair them on next poweroff/reset ***"
-echo "*******************************************************"
-echo -e -n $NC
diff --git a/arch/sandbox/board/hostfile.c b/arch/sandbox/board/hostfile.c
index 4fdf2b317d..f110621979 100644
--- a/arch/sandbox/board/hostfile.c
+++ b/arch/sandbox/board/hostfile.c
@@ -182,7 +182,7 @@ static struct driver_d hf_drv = {
.of_compatible = DRV_OF_COMPAT(hostfile_dt_ids),
.probe = hf_probe,
};
-device_platform_driver(hf_drv);
+postcore_platform_driver(hf_drv);
static int of_hostfile_fixup(struct device_node *root, void *ctx)
{
@@ -232,12 +232,21 @@ static int of_hostfile_map_fixup(struct device_node *root, void *ctx)
for_each_compatible_node_from(node, root, NULL, hostfile_dt_ids->compatible) {
struct hf_info hf = {};
uint64_t reg[2] = {};
- bool no_filename;
hf.devname = node->name;
ret = of_property_read_string(node, "barebox,filename", &hf.filename);
- no_filename = ret;
+ if (ret) {
+ pr_err("skipping nameless hostfile %s\n", hf.devname);
+ continue;
+ }
+
+ if (memcmp(hf.filename, "$build/", 7) == 0) {
+ char *fullpath = xasprintf("%s/%s", linux_get_builddir(),
+ hf.filename + sizeof "$build/" - 1);
+
+ hf.filename = fullpath;
+ }
hf.is_blockdev = of_property_read_bool(node, "barebox,blockdev");
hf.is_cdev = of_property_read_bool(node, "barebox,cdev");
@@ -263,12 +272,6 @@ static int of_hostfile_map_fixup(struct device_node *root, void *ctx)
if (ret)
goto out;
- if (no_filename) {
- ret = of_property_write_string(node, "barebox,filename", hf.filename);
- if (ret)
- goto out;
- }
-
ret = of_property_write_u32(node, "barebox,fd", hf.fd);
out:
if (ret)
diff --git a/arch/sandbox/board/power.c b/arch/sandbox/board/power.c
index 3cc9447958..3112c80348 100644
--- a/arch/sandbox/board/power.c
+++ b/arch/sandbox/board/power.c
@@ -4,11 +4,11 @@
#include <restart.h>
#include <mach/linux.h>
#include <reset_source.h>
-#include <mfd/syscon.h>
+#include <linux/nvmem-consumer.h>
struct sandbox_power {
struct restart_handler rst_hang, rst_reexec;
- struct regmap *src;
+ struct nvmem_cell *reset_source_cell;
u32 src_offset;
};
@@ -24,16 +24,20 @@ static void sandbox_rst_hang(struct restart_handler *rst)
static void sandbox_rst_reexec(struct restart_handler *rst)
{
+ u8 reason = RESET_RST;
struct sandbox_power *power = container_of(rst, struct sandbox_power, rst_reexec);
- regmap_update_bits(power->src, power->src_offset, 0xff, RESET_RST);
+
+ if (!IS_ERR(power->reset_source_cell))
+ WARN_ON(nvmem_cell_write(power->reset_source_cell, &reason, 1) <= 0);
+
linux_reexec();
}
static int sandbox_power_probe(struct device_d *dev)
{
struct sandbox_power *power = xzalloc(sizeof(*power));
- unsigned int rst;
- int ret;
+ size_t len = 1;
+ u8 *rst;
poweroff_handler_register_fn(sandbox_poweroff);
@@ -52,20 +56,19 @@ static int sandbox_power_probe(struct device_d *dev)
if (IS_ENABLED(CONFIG_SANDBOX_REEXEC))
restart_handler_register(&power->rst_reexec);
- power->src = syscon_regmap_lookup_by_phandle(dev->device_node, "barebox,reset-source");
- if (IS_ERR(power->src))
+ power->reset_source_cell = of_nvmem_cell_get(dev->device_node, "reset-source");
+ if (IS_ERR(power->reset_source_cell)) {
+ dev_warn(dev, "No reset source info available: %pe\n", power->reset_source_cell);
return 0;
+ }
- ret = of_property_read_u32_index(dev->device_node, "barebox,reset-source", 1,
- &power->src_offset);
- if (ret)
- return 0;
+ rst = nvmem_cell_read(power->reset_source_cell, &len);
+ if (!IS_ERR(rst)) {
+ reset_source_set_prinst(*rst, RESET_SOURCE_DEFAULT_PRIORITY, 0);
- ret = regmap_read(power->src, power->src_offset, &rst);
- if (ret == 0 && rst == 0)
- rst = RESET_POR;
+ free(rst);
+ }
- reset_source_set_prinst(rst, RESET_SOURCE_DEFAULT_PRIORITY, 0);
return 0;
}
diff --git a/arch/sandbox/board/stickypage.S b/arch/sandbox/board/stickypage.S
new file mode 100644
index 0000000000..f1915ab986
--- /dev/null
+++ b/arch/sandbox/board/stickypage.S
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+.globl stickypage;
+stickypage:
+
+/* nvmem */ .org 0x300
+.byte 0x01
+
+/* env */ .org 0x400
+.byte 0x79, 0xba, 0x8f, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+.byte 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x69, 0x9c, 0x7f, 0x00, 0x00, 0x00, 0x00
+
+/* state */ .org 0xC00
+.byte 0xf3, 0xfd, 0x54, 0x23, 0x18, 0x00, 0x00, 0x00, 0xa6, 0x86, 0x3b, 0xaa, 0x00, 0x00, 0x08, 0x00
+.byte 0x19, 0x70, 0x3d, 0xbb, 0x64, 0x89, 0x3b, 0x31, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00
+.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+.byte 0xf3, 0xfd, 0x54, 0x23, 0x18, 0x00, 0x00, 0x00, 0xa6, 0x86, 0x3b, 0xaa, 0x00, 0x00, 0x08, 0x00
+.byte 0x19, 0x70, 0x3d, 0xbb, 0x64, 0x89, 0x3b, 0x31, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00
+.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+.byte 0xf3, 0xfd, 0x54, 0x23, 0x18, 0x00, 0x00, 0x00, 0xa6, 0x86, 0x3b, 0xaa, 0x00, 0x00, 0x08, 0x00
+.byte 0x19, 0x70, 0x3d, 0xbb, 0x64, 0x89, 0x3b, 0x31, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00
+
+.fill 4096-(.-stickypage), 1, 0
+.size stickypage, 4096
diff --git a/arch/sandbox/board/watchdog.c b/arch/sandbox/board/watchdog.c
index e1cff7a0bf..ff26a2019f 100644
--- a/arch/sandbox/board/watchdog.c
+++ b/arch/sandbox/board/watchdog.c
@@ -6,7 +6,7 @@
#include <mach/linux.h>
#include <of.h>
#include <watchdog.h>
-#include <mfd/syscon.h>
+#include <linux/nvmem-consumer.h>
#include <reset_source.h>
struct sandbox_watchdog {
@@ -36,10 +36,9 @@ static int sandbox_watchdog_set_timeout(struct watchdog *wdd, unsigned int timeo
static int sandbox_watchdog_probe(struct device_d *dev)
{
struct device_node *np = dev->device_node;
+ struct nvmem_cell *reset_source_cell;
struct sandbox_watchdog *wd;
struct watchdog *wdd;
- struct regmap *src;
- u32 src_offset;
int ret;
wd = xzalloc(sizeof(*wd));
@@ -57,16 +56,17 @@ static int sandbox_watchdog_probe(struct device_d *dev)
return ret;
}
- src = syscon_regmap_lookup_by_phandle(np, "barebox,reset-source");
- if (IS_ERR(src))
- return 0;
+ reset_source_cell = of_nvmem_cell_get(dev->device_node, "reset-source");
+ if (IS_ERR(reset_source_cell)) {
+ dev_warn(dev, "No reset source info available: %pe\n", reset_source_cell);
+ goto out;
+ }
- ret = of_property_read_u32_index(np, "barebox,reset-source", 1, &src_offset);
- if (ret)
- return 0;
+ nvmem_cell_write(reset_source_cell, &(u8) { RESET_WDG }, 1);
- regmap_update_bits(src, src_offset, 0xff, RESET_WDG);
+ nvmem_cell_put(reset_source_cell);
+out:
dev_info(dev, "probed\n");
return 0;
}
diff --git a/arch/sandbox/configs/sandbox_defconfig b/arch/sandbox/configs/sandbox_defconfig
index d9d96d9481..881762444b 100644
--- a/arch/sandbox/configs/sandbox_defconfig
+++ b/arch/sandbox/configs/sandbox_defconfig
@@ -4,7 +4,6 @@ CONFIG_CMDLINE_EDITING=y
CONFIG_AUTO_COMPLETE=y
CONFIG_MENU=y
CONFIG_CONSOLE_ALLOW_COLOR=y
-CONFIG_PARTITION=y
CONFIG_PARTITION_DISK_EFI=y
CONFIG_DEFAULT_COMPRESSION_GZIP=y
CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW=y
@@ -126,6 +125,7 @@ CONFIG_WATCHDOG_POLLER=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_DS1307=y
CONFIG_SYSCON_REBOOT_MODE=y
+CONFIG_NVMEM_REBOOT_MODE=y
CONFIG_FS_CRAMFS=y
CONFIG_FS_EXT4=y
CONFIG_FS_TFTP=y
diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts
index e99986bb90..5b2cab219e 100644
--- a/arch/sandbox/dts/sandbox.dts
+++ b/arch/sandbox/dts/sandbox.dts
@@ -55,15 +55,17 @@
stickypage: stickypage {
compatible = "barebox,hostfile", "syscon", "simple-mfd";
+ barebox,filename = "$build/stickypage.bin";
reg = <0 0 0 4096>;
barebox,cdev; /* no caching allowed */
bmode: reboot-mode {
- compatible = "syscon-reboot-mode";
- offset = <0>;
- mask = <0xffffff00>;
- mode-normal = <0x00000000>;
- mode-loader = <0xbbbbbb00>;
+ compatible = "nvmem-reboot-mode";
+ nvmem-cells = <&reboot_mode>;
+ nvmem-cell-names = "reboot-mode";
+
+ mode-normal = <0x000000>;
+ mode-loader = <0xbbbbbb>;
};
partitions {
@@ -71,14 +73,28 @@
#address-cells = <1>;
#size-cells = <1>;
- /* 0x00+4 reserved for syscon use */
+ part_nvmem: nvmem@300 {
+ compatible = "nvmem-cells";
+ reg = <0x300 0x100>;
+ label = "nvmem";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ reset_source: reset-source@0 {
+ reg = <0x0 0x1>;
+ };
+
+ reboot_mode: reboot-mode@1 {
+ reg = <0x1 0x4>;
+ };
+ };
part_env: env@400 {
reg = <0x400 0x800>;
label = "env";
};
- part_state: state@800 {
+ part_state: state@c00 {
reg = <0xC00 0x400>;
label = "state";
};
@@ -87,12 +103,14 @@
power {
compatible = "barebox,sandbox-power";
- barebox,reset-source = <&stickypage 0>;
+ nvmem-cell-names = "reset-source";
+ nvmem-cells = <&reset_source>;
};
watchdog {
compatible = "barebox,sandbox-watchdog";
- barebox,reset-source = <&stickypage 0>;
+ nvmem-cell-names = "reset-source";
+ nvmem-cells = <&reset_source>;
};
sound {
diff --git a/arch/sandbox/mach-sandbox/include/mach/linux.h b/arch/sandbox/mach-sandbox/include/mach/linux.h
index 831e170d90..453813952e 100644
--- a/arch/sandbox/mach-sandbox/include/mach/linux.h
+++ b/arch/sandbox/mach-sandbox/include/mach/linux.h
@@ -13,6 +13,7 @@ int linux_register_device(const char *name, void *start, void *end);
int tap_alloc(const char *dev);
uint64_t linux_get_time(void);
int linux_open(const char *filename, int readwrite);
+const char *linux_get_builddir(void);
int linux_open_hostfile(struct hf_info *hf);
int linux_read(int fd, void *buf, size_t count);
int linux_read_nonblock(int fd, void *buf, size_t count);
diff --git a/arch/sandbox/os/common.c b/arch/sandbox/os/common.c
index 4eb6d37fff..e36e3972bc 100644
--- a/arch/sandbox/os/common.c
+++ b/arch/sandbox/os/common.c
@@ -127,9 +127,23 @@ void __attribute__((noreturn)) linux_exit(void)
exit(0);
}
-static size_t saved_argv_len;
static char **saved_argv;
+static int selfpath(char *buf, size_t len)
+{
+ int ret;
+
+ /* we must follow the symlink, so we can exec an updated executable */
+ ret = readlink("/proc/self/exe", buf, len - 1);
+ if (ret < 0)
+ return ret;
+
+ if (0 < ret && ret < len - 1)
+ buf[ret] = '\0';
+
+ return ret;
+}
+
void linux_reexec(void)
{
char buf[4097];
@@ -138,9 +152,8 @@ void linux_reexec(void)
cookmode();
/* we must follow the symlink, so we can exec an updated executable */
- ret = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
- if (0 < ret && ret < sizeof(buf) - 1) {
- buf[ret] = '\0';
+ ret = selfpath(buf, sizeof(buf));
+ if (ret > 0) {
execv(buf, saved_argv);
if (!strcmp(&buf[ret - DELETED_OFFSET], " (deleted)")) {
printf("barebox image on disk changed. Loading new.\n");
@@ -317,6 +330,21 @@ static int add_image(const char *_str, char *devname_template, int *devname_numb
return ret;
}
+const char *linux_get_builddir(void)
+{
+ static char path[4097];
+ int ret;
+
+ if (!path[0]) {
+ ret = selfpath(path, sizeof(path));
+ if (ret < 0)
+ return NULL;
+ dirname(path);
+ }
+
+ return path;
+}
+
int linux_open_hostfile(struct hf_info *hf)
{
char *buf = NULL;
@@ -327,45 +355,10 @@ int linux_open_hostfile(struct hf_info *hf)
hf->filename ? "" : "initially un", hf->filename ?: "",
hf->is_readonly ? "(ro)" : "");
- if (hf->filename) {
- fd = hf->fd = open(hf->filename, (hf->is_readonly ? O_RDONLY : O_RDWR) | O_CLOEXEC);
- } else {
- char *filename;
- int ret;
-
- ret = asprintf(&buf, "--image=%s=/tmp/barebox-hostfileXXXXXX", hf->devname);
- if (ret < 0) {
- perror("asprintf");
- goto err_out;
- }
-
- filename = buf + strlen("--image==") + strlen(hf->devname);
-
- fd = hf->fd = mkstemp(filename);
- if (fd >= 0) {
- ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
- if (ret < 0) {
- perror("fcntl");
- goto err_out;
- }
-
- ret = ftruncate(fd, hf->size);
- if (ret < 0) {
- perror("ftruncate");
- goto err_out;
- }
-
- hf->filename = filename;
-
- saved_argv = realloc(saved_argv,
- ++saved_argv_len * sizeof(*saved_argv));
- if (!saved_argv)
- exit(1);
- saved_argv[saved_argv_len - 2] = buf;
- saved_argv[saved_argv_len - 1] = NULL;
- }
- }
+ if (!hf->filename)
+ return -ENOENT;
+ fd = hf->fd = open(hf->filename, (hf->is_readonly ? O_RDONLY : O_RDWR) | O_CLOEXEC);
if (fd < 0) {
perror("open");
goto err_out;
@@ -517,11 +510,7 @@ int main(int argc, char *argv[])
}
}
- saved_argv_len = argc + 1;
- saved_argv = calloc(saved_argv_len, sizeof(*saved_argv));
- if (!saved_argv)
- exit(1);
- memcpy(saved_argv, argv, saved_argv_len * sizeof(*saved_argv));
+ saved_argv = argv;
ram = malloc(malloc_size);
if (!ram) {
diff --git a/commands/Kconfig b/commands/Kconfig
index 6da68a7f14..2237536088 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -206,6 +206,13 @@ config CMD_REGULATOR
the regulator command lists the currently registered regulators and
their current state.
+config CMD_NVMEM
+ bool
+ depends on NVMEM
+ prompt "nvmem command"
+ help
+ the nvmem command lists the currently registered nvmem devices.
+
config CMD_LSPCI
bool
depends on PCI
diff --git a/commands/Makefile b/commands/Makefile
index 4b45d266fd..ba5ea19eb2 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_CMD_SAVEENV) += saveenv.o
obj-$(CONFIG_CMD_LOADENV) += loadenv.o
obj-$(CONFIG_CMD_NAND) += nand.o
obj-$(CONFIG_CMD_NANDTEST) += nandtest.o
+obj-$(CONFIG_CMD_NVMEM) += nvmem.o
obj-$(CONFIG_CMD_MEMTEST) += memtest.o
obj-$(CONFIG_CMD_MEMTESTER) += memtester/
obj-$(CONFIG_CMD_TRUE) += true.o
diff --git a/commands/hwclock.c b/commands/hwclock.c
index 1b5c2cd100..7a5600c456 100644
--- a/commands/hwclock.c
+++ b/commands/hwclock.c
@@ -9,14 +9,6 @@
#include <string.h>
#include <environment.h>
-static char *strchrnul(const char *s, int c)
-{
- while (*s != '\0' && *s != c)
- s++;
-
- return (char *)s;
-}
-
static int sscanf_two_digits(char *s, int *res)
{
char buf[3];
diff --git a/commands/md.c b/commands/md.c
index ef6a1e1bc0..d80c7cca0c 100644
--- a/commands/md.c
+++ b/commands/md.c
@@ -59,7 +59,7 @@ static int do_mem_md(int argc, char *argv[])
goto out;
}
- buf = xmalloc(RW_BUF_SIZE);
+ buf = xzalloc(RW_BUF_SIZE + 7);
do {
now = min(size, (loff_t)RW_BUF_SIZE);
diff --git a/commands/nvmem.c b/commands/nvmem.c
new file mode 100644
index 0000000000..a0e3d092e3
--- /dev/null
+++ b/commands/nvmem.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: © 2021 Ahmad Fatoum, Pengutronix
+
+#include <common.h>
+#include <command.h>
+#include <linux/nvmem-consumer.h>
+
+static int do_nvmem(int argc, char *argv[])
+{
+ nvmem_devices_print();
+
+ return 0;
+}
+
+BAREBOX_CMD_HELP_START(nvmem)
+BAREBOX_CMD_HELP_TEXT("Usage: nvmem")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(nvmem)
+ .cmd = do_nvmem,
+ BAREBOX_CMD_DESC("list nvmem devices")
+ BAREBOX_CMD_GROUP(CMD_GRP_HWMANIP)
+ BAREBOX_CMD_HELP(cmd_nvmem_help)
+BAREBOX_CMD_END
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/Kconfig b/common/Kconfig
index d4e518bf8a..2d7888b9fa 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -1477,6 +1477,10 @@ config DEBUG_INITCALLS
help
If enabled this will print initcall traces.
+config DEBUG_PROBES
+ bool "Trace driver probes"
+ help
+ If enabled this will print driver probe traces.
config PBL_BREAK
bool "Execute software break on pbl start"
diff --git a/common/Makefile b/common/Makefile
index 382a4f661f..daa022ff54 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -3,6 +3,7 @@ obj-y += memory_display.o
pbl-$(CONFIG_PBL_CONSOLE) += memory_display.o
obj-y += clock.o
obj-y += console_common.o
+obj-y += deep-probe.o
obj-y += startup.o
obj-y += misc.o
obj-pbl-y += memsize.o
diff --git a/common/bbu.c b/common/bbu.c
index 1a1edda96b..cd7bdc40b7 100644
--- a/common/bbu.c
+++ b/common/bbu.c
@@ -4,6 +4,9 @@
*
* Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
*/
+
+#define pr_fmt(fmt) "bbu: " fmt
+
#include <common.h>
#include <bbu.h>
#include <linux/list.h>
@@ -32,12 +35,32 @@ static void append_bbu_entry(struct bbu_handler *handler, struct file_list *file
free(name);
}
+static bool bbu_handler_is_available(struct bbu_handler *handler)
+{
+ struct stat s;
+ int ret;
+
+ device_detect_by_name(devpath_to_name(handler->devicefile));
+
+ ret = stat(handler->devicefile, &s);
+ if (ret)
+ return false;
+
+ return true;
+}
+
void bbu_append_handlers_to_file_list(struct file_list *files)
{
struct bbu_handler *handler;
- list_for_each_entry(handler, &bbu_image_handlers, list)
- append_bbu_entry(handler, files);
+ list_for_each_entry(handler, &bbu_image_handlers, list) {
+ if (bbu_handler_is_available(handler)) {
+ append_bbu_entry(handler, files);
+ } else {
+ pr_info("Skipping unavailable handler bbu-%s\n",
+ handler->name);
+ }
+ }
}
int bbu_force(struct bbu_data *data, const char *fmt, ...)
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/console.c b/common/console.c
index 8a0af75a1f..ad1a6aaab2 100644
--- a/common/console.c
+++ b/common/console.c
@@ -220,7 +220,7 @@ static void console_init_early(void)
initialized = CONSOLE_INITIALIZED_BUFFER;
}
-static void console_set_stdoutpath(struct console_device *cdev)
+static void console_set_stdoutpath(struct console_device *cdev, unsigned baudrate)
{
int id;
char *str;
@@ -232,8 +232,7 @@ static void console_set_stdoutpath(struct console_device *cdev)
if (id < 0)
return;
- str = basprintf("console=%s%d,%dn8", cdev->linux_console_name, id,
- cdev->baudrate);
+ str = basprintf("console=%s%d,%dn8", cdev->linux_console_name, id, baudrate);
globalvar_add_simple("linux.bootargs.console", str);
@@ -297,6 +296,7 @@ int console_register(struct console_device *newcdev)
struct device_node *serdev_node = console_is_serdev_node(newcdev);
struct device_d *dev = &newcdev->class_dev;
int activate = 0, ret;
+ unsigned baudrate = CONFIG_BAUDRATE;
if (!serdev_node && initialized == CONSOLE_UNINITIALIZED)
console_init_early();
@@ -327,11 +327,16 @@ int console_register(struct console_device *newcdev)
if (serdev_node)
return of_platform_populate(serdev_node, NULL, dev);
+ if (newcdev->dev && of_device_is_stdout_path(newcdev->dev, &baudrate)) {
+ activate = 1;
+ console_set_stdoutpath(newcdev, baudrate);
+ }
+
if (newcdev->setbrg) {
- ret = newcdev->setbrg(newcdev, CONFIG_BAUDRATE);
+ ret = newcdev->setbrg(newcdev, baudrate);
if (ret)
return ret;
- newcdev->baudrate_param = newcdev->baudrate = CONFIG_BAUDRATE;
+ newcdev->baudrate_param = newcdev->baudrate = baudrate;
dev_add_param_uint32(dev, "baudrate", console_baudrate_set,
NULL, &newcdev->baudrate_param, "%u", newcdev);
}
@@ -349,11 +354,6 @@ int console_register(struct console_device *newcdev)
activate = 1;
}
- if (newcdev->dev && of_device_is_stdout_path(newcdev->dev)) {
- activate = 1;
- console_set_stdoutpath(newcdev);
- }
-
list_add_tail(&newcdev->list, &console_list);
if (activate)
diff --git a/common/deep-probe.c b/common/deep-probe.c
new file mode 100644
index 0000000000..1020ad93b7
--- /dev/null
+++ b/common/deep-probe.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <common.h>
+#include <deep-probe.h>
+#include <of.h>
+
+enum deep_probe_state {
+ DEEP_PROBE_UNKONWN = -1,
+ DEEP_PROBE_NOT_SUPPORTED,
+ DEEP_PROBE_SUPPORTED
+};
+
+static enum deep_probe_state boardstate = DEEP_PROBE_UNKONWN;
+
+bool deep_probe_is_supported(void)
+{
+ struct deep_probe_entry *board;
+
+ if (boardstate > DEEP_PROBE_UNKONWN)
+ return boardstate;
+
+ /* determine boardstate */
+ for (board = &__barebox_deep_probe_start;
+ board != &__barebox_deep_probe_end; board++) {
+ const struct of_device_id *matches = board->device_id;
+
+ for (; matches->compatible; matches++) {
+ if (of_machine_is_compatible(matches->compatible)) {
+ boardstate = DEEP_PROBE_SUPPORTED;
+ printk("Deep probe supported due to %s\n", matches->compatible);
+ return true;
+ }
+ }
+ }
+
+ boardstate = DEEP_PROBE_NOT_SUPPORTED;
+ return false;
+}
+EXPORT_SYMBOL_GPL(deep_probe_is_supported);
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 1fcc5277c5..962fff244d 100644
--- a/common/oftree.c
+++ b/common/oftree.c
@@ -343,6 +343,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)
@@ -370,7 +372,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 aeb097e1f5..9958b748eb 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/base/driver.c b/drivers/base/driver.c
index 1fa3f6c6fa..0e04ef3686 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -21,6 +21,7 @@
#include <common.h>
#include <command.h>
+#include <deep-probe.h>
#include <driver.h>
#include <malloc.h>
#include <console.h>
@@ -35,6 +36,12 @@
#include <pinctrl.h>
#include <linux/clk/clk-conf.h>
+#ifdef CONFIG_DEBUG_PROBES
+#define pr_report_probe pr_info
+#else
+#define pr_report_probe pr_debug
+#endif
+
LIST_HEAD(device_list);
EXPORT_SYMBOL(device_list);
@@ -81,8 +88,13 @@ int get_free_deviceid(const char *name_template)
int device_probe(struct device_d *dev)
{
+ static int depth = 0;
int ret;
+ depth++;
+
+ pr_report_probe("%*sprobe-> %s\n", depth * 4, "", dev_name(dev));
+
pinctrl_select_state_default(dev);
of_clk_set_defaults(dev->device_node, false);
@@ -90,13 +102,21 @@ int device_probe(struct device_d *dev)
ret = dev->bus->probe(dev);
if (ret == 0)
- return 0;
+ goto out;
if (ret == -EPROBE_DEFER) {
list_del(&dev->active);
list_add(&dev->active, &deferred);
- dev_dbg(dev, "probe deferred\n");
- return ret;
+
+ /*
+ * -EPROBE_DEFER should never appear on a deep-probe machine so
+ * inform the user immediately.
+ */
+ if (deep_probe_is_supported())
+ dev_err(dev, "probe deferred\n");
+ else
+ dev_dbg(dev, "probe deferred\n");
+ goto out;
}
list_del(&dev->active);
@@ -107,6 +127,8 @@ int device_probe(struct device_d *dev)
else
dev_err(dev, "probe failed: %s\n", strerror(-ret));
+out:
+ depth--;
return ret;
}
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index ba726c342c..f9e771e5ea 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -593,6 +593,8 @@ int of_clk_add_provider(struct device_node *np,
list_add(&cp->link, &of_clk_providers);
pr_debug("Added clock from %s\n", np ? np->full_name : "<none>");
+ of_clk_set_defaults(np, true);
+
return 0;
}
EXPORT_SYMBOL_GPL(of_clk_add_provider);
@@ -619,6 +621,11 @@ struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec)
{
struct of_clk_provider *provider;
struct clk *clk = ERR_PTR(-EPROBE_DEFER);
+ int ret;
+
+ ret = of_device_ensure_probed(clkspec->np);
+ if (ret)
+ return ERR_PTR(ret);
/* Check if we have such a provider in our array */
list_for_each_entry(provider, &of_clk_providers, link) {
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/hab/habv4.c b/drivers/hab/habv4.c
index c2acb81369..d58768fa54 100644
--- a/drivers/hab/habv4.c
+++ b/drivers/hab/habv4.c
@@ -553,8 +553,7 @@ static int habv4_get_status(const struct habv4_rvt *rvt)
break;
/* suppress RNG self-test fail events if they can be handled in software */
- if (IS_ENABLED(CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_SELF_TEST) &&
- is_known_rng_fail_event(data, len)) {
+ if (is_known_rng_fail_event(data, len)) {
pr_debug("RNG self-test failure detected, will run software self-test\n");
} else {
pr_err("-------- HAB Event %d --------\n", i);
diff --git a/drivers/i2c/i2c.c b/drivers/i2c/i2c.c
index 57d8c7017f..12aac42794 100644
--- a/drivers/i2c/i2c.c
+++ b/drivers/i2c/i2c.c
@@ -407,6 +407,9 @@ static struct i2c_client *i2c_new_device(struct i2c_adapter *adapter,
}
client->dev.info = i2c_info;
+ if (chip->of_node)
+ chip->of_node->dev = &client->dev;
+
return client;
}
@@ -548,6 +551,11 @@ struct i2c_adapter *i2c_get_adapter(int busnum)
struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node)
{
struct i2c_adapter *adap;
+ int ret;
+
+ ret = of_device_ensure_probed(node);
+ if (ret)
+ return ERR_PTR(ret);
for_each_i2c_adapter(adap)
if (adap->dev.device_node == node)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index b37017372f..4c90ad9757 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -5646,6 +5646,7 @@ int nand_scan_tail(struct nand_chip *chip)
if (!ecc->write_oob)
ecc->write_oob = nand_write_oob_syndrome;
} else if (ecc->mode == NAND_ECC_HW_SYNDROME) {
+ WARN(1, "CONFIG_NAND_ECC_HW_SYNDROME not enabled\n");
ret = -ENOSYS;
goto err_nand_manuf_cleanup;
}
diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
index 377d6c8b15..3781f7a839 100644
--- a/drivers/nvmem/Kconfig
+++ b/drivers/nvmem/Kconfig
@@ -9,6 +9,12 @@ menuconfig NVMEM
if NVMEM
+config NVMEM_RMEM
+ bool "Reserved Memory Based Driver Support"
+ help
+ This driver maps reserved memory into an nvmem device. It might be
+ useful to expose information left by firmware in memory.
+
config NVMEM_SNVS_LPGPR
tristate "Freescale SNVS LPGPR support"
select MFD_SYSCON
diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile
index 83ff3d23cb..55507f5441 100644
--- a/drivers/nvmem/Makefile
+++ b/drivers/nvmem/Makefile
@@ -3,7 +3,9 @@
#
obj-$(CONFIG_NVMEM) += nvmem_core.o
-nvmem_core-y := core.o regmap.o
+nvmem_core-y := core.o regmap.o partition.o
+
+obj-$(CONFIG_NVMEM_RMEM) += rmem.o
# Devices
obj-$(CONFIG_NVMEM_SNVS_LPGPR) += nvmem_snvs_lpgpr.o
diff --git a/drivers/nvmem/bsec.c b/drivers/nvmem/bsec.c
index 509a5fa872..d9b38c8414 100644
--- a/drivers/nvmem/bsec.c
+++ b/drivers/nvmem/bsec.c
@@ -23,7 +23,6 @@
struct bsec_priv {
u32 svc_id;
struct regmap_config map_config;
- struct nvmem_config config;
};
struct stm32_bsec_data {
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index cfeecf70cd..4e558e1650 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -49,6 +49,15 @@ struct nvmem_cell {
static LIST_HEAD(nvmem_cells);
static LIST_HEAD(nvmem_devs);
+void nvmem_devices_print(void)
+{
+ struct nvmem_device *dev;
+
+ list_for_each_entry(dev, &nvmem_devs, node) {
+ printf("%s\n", dev_name(&dev->dev));
+ }
+}
+
static ssize_t nvmem_cdev_read(struct cdev *cdev, void *buf, size_t count,
loff_t offset, unsigned long flags)
{
@@ -205,17 +214,17 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
nvmem->size = config->size;
nvmem->dev.parent = config->dev;
nvmem->bus = config->bus;
- np = config->dev->device_node;
+ np = config->cdev ? config->cdev->device_node : config->dev->device_node;
nvmem->dev.device_node = np;
nvmem->priv = config->priv;
- nvmem->read_only = of_property_read_bool(np, "read-only") |
- config->read_only;
+ if (config->read_only || !config->bus->write || of_property_read_bool(np, "read-only"))
+ nvmem->read_only = true;
dev_set_name(&nvmem->dev, config->name);
nvmem->dev.id = DEVICE_ID_DYNAMIC;
- dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name);
+ dev_dbg(nvmem->dev.parent, "Registering nvmem device %s\n", config->name);
rval = register_device(&nvmem->dev);
if (rval) {
@@ -223,10 +232,12 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
return ERR_PTR(rval);
}
- rval = nvmem_register_cdev(nvmem, config->name);
- if (rval) {
- kfree(nvmem);
- return ERR_PTR(rval);
+ if (!config->cdev) {
+ rval = nvmem_register_cdev(nvmem, config->name);
+ if (rval) {
+ kfree(nvmem);
+ return ERR_PTR(rval);
+ }
}
list_add_tail(&nvmem->node, &nvmem_devs);
diff --git a/drivers/nvmem/partition.c b/drivers/nvmem/partition.c
new file mode 100644
index 0000000000..3f0bdc58de
--- /dev/null
+++ b/drivers/nvmem/partition.c
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <common.h>
+#include <driver.h>
+#include <malloc.h>
+#include <xfuncs.h>
+#include <errno.h>
+#include <init.h>
+#include <io.h>
+#include <linux/nvmem-provider.h>
+
+static int nvmem_cdev_write(void *ctx, unsigned offset, const void *val, size_t bytes)
+{
+ return cdev_write(ctx, val, bytes, offset, 0);
+}
+
+static int nvmem_cdev_read(void *ctx, unsigned offset, void *buf, size_t bytes)
+{
+ return cdev_read(ctx, buf, bytes, offset, 0);
+}
+
+static struct nvmem_bus nvmem_cdev_bus = {
+ .read = nvmem_cdev_read,
+ .write = nvmem_cdev_write,
+};
+
+struct nvmem_device *nvmem_partition_register(struct cdev *cdev)
+{
+ struct nvmem_config config = {};
+
+ config.name = cdev->name;
+ config.dev = cdev->dev;
+ config.cdev = cdev;
+ config.priv = cdev;
+ config.stride = 1;
+ config.word_size = 1;
+ config.size = cdev->size;
+ config.bus = &nvmem_cdev_bus;
+
+ return nvmem_register(&config);
+}
diff --git a/drivers/nvmem/rmem.c b/drivers/nvmem/rmem.c
new file mode 100644
index 0000000000..e103cec448
--- /dev/null
+++ b/drivers/nvmem/rmem.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+ */
+
+#include <io.h>
+#include <driver.h>
+#include <linux/nvmem-provider.h>
+#include <init.h>
+
+struct rmem {
+ struct device_d *dev;
+ const struct resource *mem;
+};
+
+static int rmem_read(void *context, unsigned int offset,
+ void *val, size_t bytes)
+{
+ struct rmem *rmem = context;
+ return mem_copy(rmem->dev, val, (void *)rmem->mem->start + offset,
+ bytes, offset, 0);
+}
+
+static struct nvmem_bus rmem_nvmem_bus = {
+ .read = rmem_read,
+};
+
+static int rmem_probe(struct device_d *dev)
+{
+ struct nvmem_config config = { };
+ struct resource *mem;
+ struct rmem *priv;
+
+ mem = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(mem))
+ return PTR_ERR(mem);
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->mem = mem;
+
+ config.dev = priv->dev = dev;
+ config.priv = priv;
+ config.name = "rmem";
+ config.size = resource_size(mem);
+ config.bus = &rmem_nvmem_bus;
+
+ return PTR_ERR_OR_ZERO(nvmem_register(&config));
+}
+
+static const struct of_device_id rmem_match[] = {
+ { .compatible = "nvmem-rmem", },
+ { /* sentinel */ },
+};
+
+static struct driver_d rmem_driver = {
+ .name = "rmem",
+ .of_compatible = rmem_match,
+ .probe = rmem_probe,
+};
+device_platform_driver(rmem_driver);
+
+MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de>");
+MODULE_DESCRIPTION("Reserved Memory Based nvmem Driver");
+MODULE_LICENSE("GPL");
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 193bae7fa0..42b8d24874 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -15,6 +15,7 @@
* GNU General Public License for more details.
*/
#include <common.h>
+#include <deep-probe.h>
#include <of.h>
#include <of_address.h>
#include <errno.h>
@@ -1682,6 +1683,9 @@ int of_modalias_node(struct device_node *node, char *modalias, int len)
}
EXPORT_SYMBOL_GPL(of_modalias_node);
+static struct device_node *of_chosen;
+static const char *of_model;
+
struct device_node *of_get_root_node(void)
{
return root_node;
@@ -1694,11 +1698,26 @@ int of_set_root_node(struct device_node *node)
root_node = node;
+ of_chosen = of_find_node_by_path("/chosen");
+ of_property_read_string(root_node, "model", &of_model);
+
+ if (of_model)
+ barebox_set_model(of_model);
+
of_alias_scan();
return 0;
}
+static int barebox_of_populate(void)
+{
+ if (IS_ENABLED(CONFIG_OFDEVICE) && deep_probe_is_supported())
+ return of_probe();
+
+ return 0;
+}
+of_populate_initcall(barebox_of_populate);
+
int barebox_register_of(struct device_node *root)
{
if (root_node)
@@ -1707,8 +1726,11 @@ int barebox_register_of(struct device_node *root)
of_set_root_node(root);
of_fix_tree(root);
- if (IS_ENABLED(CONFIG_OFDEVICE))
- return of_probe();
+ if (IS_ENABLED(CONFIG_OFDEVICE)) {
+ of_clk_init(root, NULL);
+ if (!deep_probe_is_supported())
+ return of_probe();
+ }
return 0;
}
@@ -1720,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);
@@ -2275,9 +2297,6 @@ int of_add_memory(struct device_node *node, bool dump)
return ret;
}
-static struct device_node *of_chosen;
-static const char *of_model;
-
const char *of_get_model(void)
{
return of_model;
@@ -2300,6 +2319,9 @@ static int of_probe_memory(void)
struct device_node *memory = root_node;
int ret = 0;
+ if (!IS_ENABLED(CONFIG_OFDEVICE))
+ return 0;
+
/* Parse all available node with "memory" device_type */
while (1) {
int err;
@@ -2315,6 +2337,7 @@ static int of_probe_memory(void)
return ret;
}
+mem_initcall(of_probe_memory);
static void of_platform_device_create_root(struct device_node *np)
{
@@ -2331,32 +2354,35 @@ static void of_platform_device_create_root(struct device_node *np)
free(dev);
}
+static const struct of_device_id reserved_mem_matches[] = {
+ { .compatible = "nvmem-rmem" },
+ {}
+};
+
int of_probe(void)
{
- struct device_node *firmware;
- int ret;
+ struct device_node *node;
if(!root_node)
return -ENODEV;
- of_chosen = of_find_node_by_path("/chosen");
- of_property_read_string(root_node, "model", &of_model);
-
- if (of_model)
- barebox_set_model(of_model);
-
- ret = of_probe_memory();
+ /*
+ * Handle certain compatibles explicitly, since we don't want to create
+ * platform_devices for every node in /reserved-memory with a
+ * "compatible",
+ */
+ for_each_matching_node(node, reserved_mem_matches)
+ of_platform_device_create(node, NULL);
- firmware = of_find_node_by_path("/firmware");
- if (firmware)
- of_platform_populate(firmware, NULL, NULL);
+ node = of_find_node_by_path("/firmware");
+ if (node)
+ of_platform_populate(node, NULL, NULL);
of_platform_device_create_root(root_node);
- of_clk_init(root_node, NULL);
of_platform_populate(root_node, of_default_bus_match_table, NULL);
- return ret;
+ return 0;
}
/**
@@ -2409,6 +2435,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);
@@ -2419,15 +2446,24 @@ 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;
struct property *p, *pt;
- struct device_d *dev;
if (!node)
return;
+ if (node == root_node) {
+ pr_err("Won't delete root device node\n");
+ return;
+ }
+
list_for_each_entry_safe(p, pt, &node->properties, list)
of_delete_property(p);
@@ -2439,28 +2475,18 @@ void of_delete_node(struct device_node *node)
list_del(&node->list);
}
- dev = of_find_device_by_node(node);
- if (dev)
- dev->device_node = NULL;
-
free(node->name);
free(node->full_name);
free(node);
-
- if (node == root_node)
- of_set_root_node(NULL);
}
-int of_device_is_stdout_path(struct device_d *dev)
+struct device_node *of_get_stdoutpath(unsigned int *baudrate)
{
struct device_node *dn;
const char *name;
const char *p;
char *q;
- if (!dev->device_node)
- return 0;
-
name = of_get_property(of_chosen, "stdout-path", NULL);
if (!name)
name = of_get_property(of_chosen, "linux,stdout-path", NULL);
@@ -2468,10 +2494,7 @@ int of_device_is_stdout_path(struct device_d *dev)
if (!name)
return 0;
- /* This could make use of strchrnul if it were available */
- p = strchr(name, ':');
- if (!p)
- p = name + strlen(name);
+ p = strchrnul(name, ':');
q = xstrndup(name, p - name);
@@ -2479,7 +2502,24 @@ int of_device_is_stdout_path(struct device_d *dev)
free(q);
- return dn == dev->device_node;
+ if (baudrate && *p) {
+ unsigned rate = simple_strtoul(p + 1, NULL, 10);
+ if (rate)
+ *baudrate = rate;
+ }
+
+ return dn;
+}
+
+int of_device_is_stdout_path(struct device_d *dev, unsigned int *baudrate)
+{
+ unsigned int tmp = *baudrate;
+
+ if (!dev || !dev->device_node || dev->device_node != of_get_stdoutpath(&tmp))
+ return false;
+
+ *baudrate = tmp;
+ return true;
}
/**
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/of_net.c b/drivers/of/of_net.c
index cee4597195..67015160e2 100644
--- a/drivers/of/of_net.c
+++ b/drivers/of/of_net.c
@@ -9,6 +9,7 @@
#include <net.h>
#include <of_net.h>
#include <linux/phy.h>
+#include <linux/nvmem-consumer.h>
/**
* It maps 'enum phy_interface_t' found in include/linux/phy.h
@@ -67,12 +68,55 @@ int of_get_phy_mode(struct device_node *np)
}
EXPORT_SYMBOL_GPL(of_get_phy_mode);
+static int of_get_mac_addr(struct device_node *np, const char *name, u8 *addr)
+{
+ struct property *pp = of_find_property(np, name, NULL);
+
+ if (pp && pp->length == ETH_ALEN && is_valid_ether_addr(pp->value)) {
+ memcpy(addr, pp->value, ETH_ALEN);
+ return 0;
+ }
+ return -ENODEV;
+}
+
+int of_get_mac_addr_nvmem(struct device_node *np, u8 *addr)
+{
+ struct nvmem_cell *cell;
+ const void *mac;
+ size_t len;
+
+ if (!IS_ENABLED(CONFIG_NVMEM))
+ return -ENODEV;
+
+ cell = of_nvmem_cell_get(np, "mac-address");
+ if (IS_ERR(cell))
+ return PTR_ERR(cell);
+
+ mac = nvmem_cell_read(cell, &len);
+ nvmem_cell_put(cell);
+
+ if (IS_ERR(mac))
+ return PTR_ERR(mac);
+
+ if (len != ETH_ALEN || !is_valid_ether_addr(mac)) {
+ kfree(mac);
+ return -EINVAL;
+ }
+
+ memcpy(addr, mac, ETH_ALEN);
+ kfree(mac);
+
+ return 0;
+}
+
/**
* Search the device tree for the best MAC address to use. 'mac-address' is
* checked first, because that is supposed to contain to "most recent" MAC
* address. If that isn't set, then 'local-mac-address' is checked next,
- * because that is the default address. If that isn't set, then the obsolete
- * 'address' is checked, just in case we're using an old device tree.
+ * because that is the default address. If that isn't set, then the obsolete
+ * 'address' is checked, just in case we're using an old device tree. If any
+ * of the above isn't set, then try to get MAC address from nvmem cell named
+ * 'mac-address'.
*
* Note that the 'address' property is supposed to contain a virtual address of
* the register set, but some DTS files have redefined that property to be the
@@ -85,18 +129,24 @@ EXPORT_SYMBOL_GPL(of_get_phy_mode);
* this case, the real MAC is in 'local-mac-address', and 'mac-address' exists
* but is all zeros.
*/
-const void *of_get_mac_address(struct device_node *np)
+int of_get_mac_address(struct device_node *np, u8 *addr)
{
- const void *p;
- int len, i;
- const char *str[] = { "mac-address", "local-mac-address", "address" };
-
- for (i = 0; i < ARRAY_SIZE(str); i++) {
- p = of_get_property(np, str[i], &len);
- if (p && (len == 6) && is_valid_ether_addr(p))
- return p;
- }
+ int ret;
+
+ if (!np)
+ return -ENODEV;
+
+ ret = of_get_mac_addr(np, "mac-address", addr);
+ if (!ret)
+ return 0;
+
+ ret = of_get_mac_addr(np, "local-mac-address", addr);
+ if (!ret)
+ return 0;
+
+ ret = of_get_mac_addr(np, "address", addr);
+ if (!ret)
+ return 0;
- return NULL;
+ return of_get_mac_addr_nvmem(np, addr);
}
-EXPORT_SYMBOL(of_get_mac_address);
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/of/partition.c b/drivers/of/partition.c
index b71716218b..b6d0523fd9 100644
--- a/drivers/of/partition.c
+++ b/drivers/of/partition.c
@@ -20,6 +20,7 @@
#include <linux/mtd/mtd.h>
#include <linux/err.h>
#include <nand.h>
+#include <linux/nvmem-provider.h>
#include <init.h>
#include <globalvar.h>
@@ -83,6 +84,12 @@ struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node)
if (new)
new->device_node = node;;
+ if (IS_ENABLED(CONFIG_NVMEM) && of_device_is_compatible(node, "nvmem-cells")) {
+ struct nvmem_device *nvmem = nvmem_partition_register(new);
+ if (IS_ERR(nvmem))
+ dev_warn(cdev->dev, "nvmem registeration failed: %pe\n", nvmem);
+ }
+
free(filename);
return new;
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 21c7cce1a5..92b6caef5a 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -15,6 +15,7 @@
* GNU General Public License for more details.
*/
#include <common.h>
+#include <deep-probe.h>
#include <malloc.h>
#include <of.h>
#include <of_address.h>
@@ -29,6 +30,12 @@
struct device_d *of_find_device_by_node(struct device_node *np)
{
struct device_d *dev;
+ int ret;
+
+ ret = of_device_ensure_probed(np);
+ if (ret)
+ return NULL;
+
for_each_device(dev)
if (dev->device_node == np)
return dev;
@@ -101,11 +108,18 @@ struct device_d *of_platform_device_create(struct device_node *np,
struct device_d *dev;
struct resource *res = NULL, temp_res;
resource_size_t resinval;
- int i, j, ret, num_reg = 0, match;
+ int i, ret, num_reg = 0;
if (!of_device_is_available(np))
return NULL;
+ /*
+ * Linux uses the OF_POPULATED flag to skip already populated/created
+ * devices.
+ */
+ if (np->dev)
+ return np->dev;
+
/* count the io resources */
if (of_can_translate_address(np))
while (of_address_to_resource(np, num_reg, &temp_res) == 0)
@@ -121,35 +135,6 @@ struct device_d *of_platform_device_create(struct device_node *np,
return NULL;
}
}
-
- /*
- * A device may already be registered as platform_device.
- * Instead of registering the same device again, just
- * add this node to the existing device.
- */
- for_each_device(dev) {
- if (!dev->resource)
- continue;
-
- for (i = 0, match = 0; i < num_reg; i++)
- for (j = 0; j < dev->num_resources; j++)
- if (dev->resource[j].start ==
- res[i].start &&
- dev->resource[j].end ==
- res[i].end) {
- match++;
- break;
- }
-
- /* check if all address resources match */
- if (match == num_reg) {
- debug("connecting %s to %s\n",
- np->name, dev_name(dev));
- dev->device_node = np;
- free(res);
- return dev;
- }
- }
}
/* setup generic device info */
@@ -169,16 +154,30 @@ struct device_d *of_platform_device_create(struct device_node *np,
__func__, dev_name(dev),
(num_reg) ? &dev->resource[0].start : &resinval);
+ BUG_ON(np->dev);
+ np->dev = dev;
+
ret = platform_device_register(dev);
if (!ret)
return dev;
+ np->dev = NULL;
+
free(dev);
if (num_reg)
free(res);
return NULL;
}
+struct driver_d dummy_driver = {
+ .name = "dummy-driver",
+};
+
+void of_platform_device_dummy_drv(struct device_d *dev)
+{
+ dev->driver = &dummy_driver;
+}
+
/**
* of_device_enable_and_register - Enable and register device
* @np: pointer to node to enable create device for
@@ -252,6 +251,13 @@ static struct device_d *of_amba_device_create(struct device_node *np)
if (!of_device_is_available(np))
return NULL;
+ /*
+ * Linux uses the OF_POPULATED flag to skip already populated/created
+ * devices.
+ */
+ if (np->dev)
+ return np->dev;
+
dev = xzalloc(sizeof(*dev));
/* setup generic device info */
@@ -275,6 +281,8 @@ static struct device_d *of_amba_device_create(struct device_node *np)
if (ret)
goto amba_err_free;
+ np->dev = &dev->dev;
+
return &dev->dev;
amba_err_free:
@@ -364,3 +372,175 @@ int of_platform_populate(struct device_node *root,
return rc;
}
EXPORT_SYMBOL_GPL(of_platform_populate);
+
+static struct device_d *of_device_create_on_demand(struct device_node *np)
+{
+ struct device_node *parent;
+ struct device_d *parent_dev, *dev;
+
+ parent = of_get_parent(np);
+ if (!parent)
+ return NULL;
+
+ if (!np->dev)
+ pr_debug("Creating device for %s\n", np->full_name);
+
+ /* Create all parent devices needed for the requested device */
+ parent_dev = parent->dev ? : of_device_create_on_demand(parent);
+ if (IS_ERR(parent_dev))
+ return parent_dev;
+
+ /*
+ * Parent devices like i2c/spi controllers are populating their own
+ * devices. So it can be that the requested device already exists after
+ * the parent device creation.
+ */
+ if (np->dev)
+ return np->dev;
+
+ if (of_device_is_compatible(np, "arm,primecell"))
+ dev = of_amba_device_create(np);
+ else
+ dev = of_platform_device_create(np, parent_dev);
+
+ return dev ? : ERR_PTR(-ENODEV);
+}
+
+/**
+ * of_device_ensure_probed() - ensures that a device is probed
+ *
+ * @np: the device_node handle which should be probed
+ *
+ * Ensures that the device is populated and probed so frameworks can make use of
+ * it.
+ *
+ * Return: %0 on success
+ * %-ENODEV if either the device can't be populated, the driver is
+ * missing or the driver probe returns an error.
+ */
+int of_device_ensure_probed(struct device_node *np)
+{
+ struct device_d *dev;
+
+ if (!deep_probe_is_supported())
+ return 0;
+
+ dev = of_device_create_on_demand(np);
+ if (IS_ERR(dev))
+ return PTR_ERR(dev);
+
+ BUG_ON(!dev);
+
+ /*
+ * The deep-probe mechanism relies on the fact that all necessary
+ * drivers are added before the device creation. Furthermore deep-probe
+ * is the answer to the EPROBE_DEFER errno so we must ensure that the
+ * driver was probed successfully after the device creation. Both
+ * requirements are fulfilled if 'dev->driver' is not NULL.
+ */
+ if (!dev->driver)
+ return -ENODEV;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_device_ensure_probed);
+
+/**
+ * of_device_ensure_probed_by_alias() - ensures that a device is probed
+ *
+ * @alias: the alias string to search for a device
+ *
+ * The function search for a given alias string and ensures that the device is
+ * populated and probed if found.
+ *
+ * Return: %0 on success
+ * %-ENODEV if either the device can't be populated, the driver is
+ * missing or the driver probe returns an error
+ * %-EINVAL if alias can't be found
+ */
+int of_device_ensure_probed_by_alias(const char *alias)
+{
+ struct device_node *dev_node;
+
+ dev_node = of_find_node_by_alias(NULL, alias);
+ if (!dev_node)
+ return -EINVAL;
+
+ return of_device_ensure_probed(dev_node);
+}
+EXPORT_SYMBOL_GPL(of_device_ensure_probed_by_alias);
+
+/**
+ * of_devices_ensure_probed_by_dev_id() - ensures that devices are probed
+ *
+ * @ids: the matching 'struct of_device_id' ids
+ *
+ * The function start searching the device tree from @np and populates and
+ * probes devices which match @ids.
+ *
+ * Return: %0 on success
+ * %-ENODEV if either the device wasn't found, can't be populated,
+ * the driver is missing or the driver probe returns an error
+ */
+int of_devices_ensure_probed_by_dev_id(const struct of_device_id *ids)
+{
+ struct device_node *np;
+ int err, ret = 0;
+
+ for_each_matching_node(np, ids) {
+ if (!of_device_is_available(np))
+ continue;
+
+ err = of_device_ensure_probed(np);
+ if (err)
+ ret = err;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(of_devices_ensure_probed_by_dev_id);
+
+/**
+ * of_devices_ensure_probed_by_property() - ensures that devices are probed
+ *
+ * @property_name: The property name to search for
+ *
+ * The function starts searching the whole device tree and populates and probes
+ * devices which matches @property_name.
+ *
+ * Return: %0 on success
+ * %-ENODEV if either the device wasn't found, can't be populated,
+ * the driver is missing or the driver probe returns an error
+ */
+int of_devices_ensure_probed_by_property(const char *property_name)
+{
+ struct device_node *node;
+
+ for_each_node_with_property(node, property_name) {
+ int ret;
+
+ ret = of_device_ensure_probed(node);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_devices_ensure_probed_by_property);
+
+static int of_stdoutpath_init(void)
+{
+ struct device_node *np;
+
+ np = of_get_stdoutpath(NULL);
+ if (!np)
+ return 0;
+
+ /*
+ * With deep probe support the device providing the console
+ * can come quite late in the probe order. Make sure it's
+ * probed now so that we get output earlier.
+ */
+ return of_device_ensure_probed(np);
+}
+postconsole_initcall(of_stdoutpath_init);
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index ff6e35d160..493876b17f 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -259,6 +259,10 @@ static struct phy *_of_phy_get(struct device_node *np, int index)
if (ret)
return ERR_PTR(-ENODEV);
+ ret = of_device_ensure_probed(args.np);
+ if (ret)
+ return ERR_PTR(ret);
+
phy_provider = of_phy_provider_lookup(args.np);
if (IS_ERR(phy_provider)) {
return ERR_CAST(phy_provider);
diff --git a/drivers/pinctrl/pinctrl-stm32.c b/drivers/pinctrl/pinctrl-stm32.c
index 09b62309f6..97a643a4da 100644
--- a/drivers/pinctrl/pinctrl-stm32.c
+++ b/drivers/pinctrl/pinctrl-stm32.c
@@ -412,11 +412,14 @@ static int stm32_pinctrl_probe(struct device_d *dev)
if (!of_property_read_bool(child, "gpio-controller"))
continue;
- ret = stm32_gpiochip_add(gpio_bank++, child, dev);
+ ret = stm32_gpiochip_add(gpio_bank, child, dev);
if (ret) {
dev_err(dev, "couldn't add gpiochip %s, ret = %d\n", child->name, ret);
return ret;
}
+
+ of_platform_device_dummy_drv(gpio_bank->chip.dev);
+ gpio_bank++;
}
dev_dbg(dev, "pinctrl/gpio driver registered\n");
diff --git a/drivers/pinctrl/pinctrl.c b/drivers/pinctrl/pinctrl.c
index b36521ef83..7e88391ff3 100644
--- a/drivers/pinctrl/pinctrl.c
+++ b/drivers/pinctrl/pinctrl.c
@@ -76,7 +76,7 @@ static struct pinctrl_device *find_pinctrl(struct device_node *node)
return NULL;
}
-static int pinctrl_config_one(struct device_node *np)
+static int pinctrl_config_one(struct device_node *for_node, struct device_node *np)
{
struct pinctrl_device *pdev;
struct device_node *pinctrl_node = np;
@@ -85,12 +85,18 @@ static int pinctrl_config_one(struct device_node *np)
pinctrl_node = pinctrl_node->parent;
if (!pinctrl_node)
return -ENODEV;
- pdev = find_pinctrl(pinctrl_node);
- if (pdev)
+ if (of_get_property(pinctrl_node, "compatible", NULL))
break;
}
- return pdev->ops->set_state(pdev, np);
+ if (pinctrl_node != for_node)
+ of_device_ensure_probed(pinctrl_node);
+
+ pdev = find_pinctrl(pinctrl_node);
+ if (pdev)
+ return pdev->ops->set_state(pdev, np);
+ else
+ return -ENODEV;
}
int of_pinctrl_select_state(struct device_node *np, const char *name)
@@ -148,7 +154,7 @@ int of_pinctrl_select_state(struct device_node *np, const char *name)
}
/* Parse the node */
- ret = pinctrl_config_one(np_config);
+ ret = pinctrl_config_one(np, np_config);
if (ret < 0)
goto err;
}
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index dec1482ccd..e4151d8bc6 100644
--- a/drivers/power/reset/Kconfig
+++ b/drivers/power/reset/Kconfig
@@ -13,6 +13,16 @@ config SYSCON_REBOOT_MODE
Say y here will enable reboot mode driver. This will
get reboot mode arguments and store it in SYSCON mapped
register, then the bootloader can read it to take different
+
+config NVMEM_REBOOT_MODE
+ bool "Generic NVMEM reboot mode driver"
+ depends on OFDEVICE
+ depends on NVMEM
+ select REBOOT_MODE
+ help
+ Say y here will enable reboot mode driver. This will
+ get reboot mode arguments and store it in a NVMEM cell,
+ then the bootloader can read it and take different
action according to the mode.
config POWER_RESET_SYSCON
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
index 33d29d2d95..10d6f2a41e 100644
--- a/drivers/power/reset/Makefile
+++ b/drivers/power/reset/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o
obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o
+obj-$(CONFIG_NVMEM_REBOOT_MODE) += nvmem-reboot-mode.o
obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o
obj-$(CONFIG_POWER_RESET_SYSCON_POWEROFF) += syscon-poweroff.o
obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
diff --git a/drivers/power/reset/nvmem-reboot-mode.c b/drivers/power/reset/nvmem-reboot-mode.c
new file mode 100644
index 0000000000..b82b37d642
--- /dev/null
+++ b/drivers/power/reset/nvmem-reboot-mode.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) Vaisala Oyj. All rights reserved.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <of.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/reboot-mode.h>
+
+struct nvmem_reboot_mode {
+ struct reboot_mode_driver reboot;
+ struct nvmem_cell *cell;
+};
+
+static int nvmem_reboot_mode_write(struct reboot_mode_driver *reboot,
+ const u32 *_magic)
+{
+ struct nvmem_reboot_mode *nvmem_rbm;
+ u32 magic = *_magic;
+ int ret;
+
+ nvmem_rbm = container_of(reboot, struct nvmem_reboot_mode, reboot);
+
+ ret = nvmem_cell_write(nvmem_rbm->cell, &magic, sizeof(magic));
+ if (ret < 0)
+ dev_err(reboot->dev, "update reboot mode bits failed: %pe\n", ERR_PTR(ret));
+ else if (ret != 4)
+ ret = -EIO;
+ else
+ ret = 0;
+
+ return ret;
+}
+
+static int nvmem_reboot_mode_probe(struct device_d *dev)
+{
+ struct nvmem_reboot_mode *nvmem_rbm;
+ struct nvmem_cell *cell;
+ void *magicbuf;
+ size_t len;
+ int ret;
+
+ cell = nvmem_cell_get(dev, "reboot-mode");
+ if (IS_ERR(cell)) {
+ ret = PTR_ERR(cell);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to get the nvmem cell reboot-mode: %pe\n", cell);
+ return ret;
+ }
+
+ nvmem_rbm = xzalloc(sizeof(*nvmem_rbm));
+
+ nvmem_rbm->cell = cell;
+ nvmem_rbm->reboot.dev = dev;
+ nvmem_rbm->reboot.write = nvmem_reboot_mode_write;
+ nvmem_rbm->reboot.priority = 200;
+
+ magicbuf = nvmem_cell_read(nvmem_rbm->cell, &len);
+ if (IS_ERR(magicbuf) || len != 4) {
+ dev_err(dev, "error reading reboot mode: %pe\n", magicbuf);
+ return PTR_ERR(magicbuf);
+ }
+
+ ret = reboot_mode_register(&nvmem_rbm->reboot, magicbuf, 1);
+ if (ret)
+ dev_err(dev, "can't register reboot mode\n");
+
+ return ret;
+}
+
+static const struct of_device_id nvmem_reboot_mode_of_match[] = {
+ { .compatible = "nvmem-reboot-mode" },
+ { /* sentinel */ }
+};
+
+static struct driver_d nvmem_reboot_mode_driver = {
+ .probe = nvmem_reboot_mode_probe,
+ .name = "nvmem-reboot-mode",
+ .of_compatible = nvmem_reboot_mode_of_match,
+};
+coredevice_platform_driver(nvmem_reboot_mode_driver);
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index ac3a9b048e..097f7d779b 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -175,6 +175,7 @@ int of_regulator_register(struct regulator_dev *rd, struct device_node *node)
return PTR_ERR(ri);
ri->node = node;
+ node->dev = rd->dev;
if (rd->desc->off_on_delay)
ri->enable_time_us = rd->desc->off_on_delay;
@@ -197,6 +198,7 @@ static struct regulator_internal *of_regulator_get(struct device_d *dev, const c
char *propname;
struct regulator_internal *ri;
struct device_node *node;
+ int ret;
propname = basprintf("%s-supply", supply);
@@ -228,6 +230,10 @@ static struct regulator_internal *of_regulator_get(struct device_d *dev, const c
goto out;
}
+ ret = of_device_ensure_probed(node);
+ if (ret)
+ return ERR_PTR(ret);
+
list_for_each_entry(ri, &regulator_list, list) {
if (ri->node == node) {
dev_dbg(dev, "Using %s regulator from %s\n",
diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c
index 160a55163f..e35b294fb2 100644
--- a/drivers/regulator/fixed.c
+++ b/drivers/regulator/fixed.c
@@ -20,10 +20,10 @@
#include <of.h>
#include <of_gpio.h>
#include <gpio.h>
+#include <gpiod.h>
struct regulator_fixed {
int gpio;
- int active_low;
int always_on;
struct regulator_dev rdev;
struct regulator_desc rdesc;
@@ -36,7 +36,7 @@ static int regulator_fixed_enable(struct regulator_dev *rdev)
if (!gpio_is_valid(fix->gpio))
return 0;
- return gpio_direction_output(fix->gpio, !fix->active_low);
+ return gpio_direction_active(fix->gpio, true);
}
static int regulator_fixed_disable(struct regulator_dev *rdev)
@@ -49,7 +49,7 @@ static int regulator_fixed_disable(struct regulator_dev *rdev)
if (!gpio_is_valid(fix->gpio))
return 0;
- return gpio_direction_output(fix->gpio, fix->active_low);
+ return gpio_direction_active(fix->gpio, false);
}
const static struct regulator_ops fixed_ops = {
@@ -60,7 +60,6 @@ const static struct regulator_ops fixed_ops = {
static int regulator_fixed_probe(struct device_d *dev)
{
struct regulator_fixed *fix;
- enum of_gpio_flags gpioflags;
int ret;
if (!dev->device_node)
@@ -70,14 +69,11 @@ static int regulator_fixed_probe(struct device_d *dev)
fix->gpio = -EINVAL;
if (of_get_property(dev->device_node, "gpio", NULL)) {
- fix->gpio = of_get_named_gpio_flags(dev->device_node, "gpio", 0, &gpioflags);
+ fix->gpio = gpiod_get(dev, NULL, GPIOD_ASIS);
if (fix->gpio < 0) {
ret = fix->gpio;
goto err;
}
-
- if (gpioflags & OF_GPIO_ACTIVE_LOW)
- fix->active_low = 1;
}
fix->rdesc.ops = &fixed_ops;
diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index 940b46d4b4..9f5a642539 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -173,6 +173,10 @@ static struct reset_control *of_reset_control_get_by_index(struct device_node *n
if (ret)
return ERR_PTR(ret);
+ ret = of_device_ensure_probed(args.np);
+ if (ret)
+ return ERR_PTR(ret);
+
rcdev = NULL;
list_for_each_entry(r, &reset_controller_list, list) {
if (args.np == r->of_node) {
@@ -209,8 +213,8 @@ static struct reset_control *of_reset_control_get_by_index(struct device_node *n
*
* Use of id names is optional.
*/
-static struct reset_control *of_reset_control_get(struct device_node *node,
- const char *id)
+struct reset_control *of_reset_control_get(struct device_node *node,
+ const char *id)
{
int index = 0;
diff --git a/drivers/reset/reset-stm32.c b/drivers/reset/reset-stm32.c
index a4498f573b..2ef00859c4 100644
--- a/drivers/reset/reset-stm32.c
+++ b/drivers/reset/reset-stm32.c
@@ -81,8 +81,8 @@ static u32 stm32_reset_status(struct stm32_reset *priv, unsigned long bank)
static void stm32_reset(struct stm32_reset *priv, unsigned long id, bool assert)
{
- int bank = (id / BITS_PER_LONG) * 4;
- int offset = id % BITS_PER_LONG;
+ int bank = (id / 32) * 4;
+ int offset = id % 32;
priv->ops->reset(priv->base + bank, offset, assert);
}
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 323d93efeb..91083ee709 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -71,7 +71,7 @@ config DRIVER_SPI_IMX_0_7
config DRIVER_SPI_IMX_2_3
bool
- depends on ARCH_IMX50 || ARCH_IMX51 || ARCH_IMX53 || ARCH_IMX6 || ARCH_IMX7 || ARCH_IMX8MQ
+ depends on ARCH_IMX50 || ARCH_IMX51 || ARCH_IMX53 || ARCH_IMX6 || ARCH_IMX7 || ARCH_IMX8M
default y
config DRIVER_SPI_MXS
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 8421d9d7c1..d1d3bdcc41 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -107,6 +107,8 @@ struct spi_device *spi_new_device(struct spi_controller *ctrl,
if (status)
goto fail;
+ chip->device_node->dev = &proxy->dev;
+
return proxy;
fail:
free(proxy);
diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c
index 10d5aa310b..e282988fb4 100644
--- a/drivers/usb/misc/usb251xb.c
+++ b/drivers/usb/misc/usb251xb.c
@@ -560,7 +560,7 @@ static int usb251xb_get_ofdata(struct usb251xb *hub,
*/
hub->port_swap = USB251XB_DEF_PORT_SWAP;
of_property_for_each_u32(np, "swap-dx-lanes", prop, p, port) {
- if ((port >= 0) && (port <= data->port_cnt))
+ if (port <= data->port_cnt)
hub->port_swap |= BIT(port);
}
diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c
index 916027ea87..8f2f772c6e 100644
--- a/drivers/w1/masters/w1-gpio.c
+++ b/drivers/w1/masters/w1-gpio.c
@@ -1,13 +1,8 @@
-/*
- * w1-gpio - GPIO w1 bus master driver
- *
- * Copyright (C) 2007 Ville Syrjala <syrjala@sci.fi>
- * Copyright (c) 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
- */
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2007 Ville Syrjala <syrjala@sci.fi>
+// SPDX-FileCopyrightText: 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
+
+/* w1-gpio - GPIO w1 bus master driver */
#include <common.h>
#include <init.h>
diff --git a/drivers/w1/slaves/w1_ds2431.c b/drivers/w1/slaves/w1_ds2431.c
index ab2ceffa42..9a5f6b5dae 100644
--- a/drivers/w1/slaves/w1_ds2431.c
+++ b/drivers/w1/slaves/w1_ds2431.c
@@ -1,13 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2008 Bernhard Weirich <bernhard.weirich@riedel.net>
+// SPDX-FileCopyrightText: 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
+
/*
* w1_ds2431.c - w1 family 2d (DS2431) driver
*
- * Copyright (c) 2008 Bernhard Weirich <bernhard.weirich@riedel.net>
- * Copyright (c) 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
- *
* Heavily inspired by w1_DS2433 driver from Ben Gardner <bgardner@wabtec.com>
- *
- * This source code is licensed under the GNU General Public License,
- * Version 2. See the file COPYING for more details.
*/
#include <init.h>
diff --git a/drivers/w1/slaves/w1_ds2433.c b/drivers/w1/slaves/w1_ds2433.c
index b24fb5b3b5..0e626530db 100644
--- a/drivers/w1/slaves/w1_ds2433.c
+++ b/drivers/w1/slaves/w1_ds2433.c
@@ -1,11 +1,7 @@
-/*
- * w1_ds2433.c - w1 family 23 (DS2433) driver
- *
- * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com>
- *
- * This source code is licensed under the GNU General Public License,
- * Version 2. See the file COPYING for more details.
- */
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2005 Ben Gardner <bgardner@wabtec.com>
+
+/* w1_ds2433.c - w1 family 23 (DS2433) driver */
#include <init.h>
#include "../w1.h"
diff --git a/dts/Bindings/connector/usb-connector.yaml b/dts/Bindings/connector/usb-connector.yaml
index 32509b9814..92b49bc379 100644
--- a/dts/Bindings/connector/usb-connector.yaml
+++ b/dts/Bindings/connector/usb-connector.yaml
@@ -149,6 +149,17 @@ properties:
maxItems: 6
$ref: /schemas/types.yaml#/definitions/uint32-array
+ sink-vdos-v1:
+ description: An array of u32 with each entry, a Vendor Defined Message Object (VDO),
+ providing additional information corresponding to the product, the detailed bit
+ definitions and the order of each VDO can be found in
+ "USB Power Delivery Specification Revision 2.0, Version 1.3" chapter 6.4.4.3.1 Discover
+ Identity. User can specify the VDO array via VDO_IDH/_CERT/_PRODUCT/_CABLE/_AMA defined in
+ dt-bindings/usb/pd.h.
+ minItems: 3
+ maxItems: 6
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+
op-sink-microwatt:
description: Sink required operating power in microwatt, if source can't
offer the power, Capability Mismatch is set. Required for power sink and
@@ -207,6 +218,10 @@ properties:
SNK_READY for non-pd link.
type: boolean
+dependencies:
+ sink-vdos-v1: [ 'sink-vdos' ]
+ sink-vdos: [ 'sink-vdos-v1' ]
+
required:
- compatible
diff --git a/dts/Bindings/hwmon/ti,ads7828.yaml b/dts/Bindings/hwmon/ti,ads7828.yaml
index 33ee575bb0..926be9a290 100644
--- a/dts/Bindings/hwmon/ti,ads7828.yaml
+++ b/dts/Bindings/hwmon/ti,ads7828.yaml
@@ -49,7 +49,7 @@ examples:
#size-cells = <0>;
adc@48 {
- comatible = "ti,ads7828";
+ compatible = "ti,ads7828";
reg = <0x48>;
vref-supply = <&vref>;
ti,differential-input;
diff --git a/dts/Bindings/media/renesas,drif.yaml b/dts/Bindings/media/renesas,drif.yaml
index ce505a7c00..9cd56ff2c3 100644
--- a/dts/Bindings/media/renesas,drif.yaml
+++ b/dts/Bindings/media/renesas,drif.yaml
@@ -67,9 +67,7 @@ properties:
maxItems: 1
clock-names:
- maxItems: 1
- items:
- - const: fck
+ const: fck
resets:
maxItems: 1
diff --git a/dts/Bindings/sound/amlogic,gx-sound-card.yaml b/dts/Bindings/sound/amlogic,gx-sound-card.yaml
index db61f0731a..2e35aeaa87 100644
--- a/dts/Bindings/sound/amlogic,gx-sound-card.yaml
+++ b/dts/Bindings/sound/amlogic,gx-sound-card.yaml
@@ -57,7 +57,7 @@ patternProperties:
rate
sound-dai:
- $ref: /schemas/types.yaml#/definitions/phandle
+ $ref: /schemas/types.yaml#/definitions/phandle-array
description: phandle of the CPU DAI
patternProperties:
@@ -71,7 +71,7 @@ patternProperties:
properties:
sound-dai:
- $ref: /schemas/types.yaml#/definitions/phandle
+ $ref: /schemas/types.yaml#/definitions/phandle-array
description: phandle of the codec DAI
required:
diff --git a/dts/include/dt-bindings/usb/pd.h b/dts/include/dt-bindings/usb/pd.h
index fef3ef6596..e6526b1381 100644
--- a/dts/include/dt-bindings/usb/pd.h
+++ b/dts/include/dt-bindings/usb/pd.h
@@ -106,6 +106,10 @@
* <20:16> :: Reserved, Shall be set to zero
* <15:0> :: USB-IF assigned VID for this cable vendor
*/
+
+/* PD Rev2.0 definition */
+#define IDH_PTYPE_UNDEF 0
+
/* SOP Product Type (UFP) */
#define IDH_PTYPE_NOT_UFP 0
#define IDH_PTYPE_HUB 1
@@ -163,10 +167,10 @@
#define UFP_VDO_VER1_2 2
/* Device Capability */
-#define DEV_USB2_CAPABLE BIT(0)
-#define DEV_USB2_BILLBOARD BIT(1)
-#define DEV_USB3_CAPABLE BIT(2)
-#define DEV_USB4_CAPABLE BIT(3)
+#define DEV_USB2_CAPABLE (1 << 0)
+#define DEV_USB2_BILLBOARD (1 << 1)
+#define DEV_USB3_CAPABLE (1 << 2)
+#define DEV_USB4_CAPABLE (1 << 3)
/* Connector Type */
#define UFP_RECEPTACLE 2
@@ -191,9 +195,9 @@
/* Alternate Modes */
#define UFP_ALTMODE_NOT_SUPP 0
-#define UFP_ALTMODE_TBT3 BIT(0)
-#define UFP_ALTMODE_RECFG BIT(1)
-#define UFP_ALTMODE_NO_RECFG BIT(2)
+#define UFP_ALTMODE_TBT3 (1 << 0)
+#define UFP_ALTMODE_RECFG (1 << 1)
+#define UFP_ALTMODE_NO_RECFG (1 << 2)
/* USB Highest Speed */
#define UFP_USB2_ONLY 0
@@ -217,9 +221,9 @@
* <4:0> :: Port number
*/
#define DFP_VDO_VER1_1 1
-#define HOST_USB2_CAPABLE BIT(0)
-#define HOST_USB3_CAPABLE BIT(1)
-#define HOST_USB4_CAPABLE BIT(2)
+#define HOST_USB2_CAPABLE (1 << 0)
+#define HOST_USB3_CAPABLE (1 << 1)
+#define HOST_USB4_CAPABLE (1 << 2)
#define DFP_RECEPTACLE 2
#define DFP_CAPTIVE 3
@@ -228,7 +232,25 @@
| ((pnum) & 0x1f))
/*
- * Passive Cable VDO
+ * Cable VDO (for both Passive and Active Cable VDO in PD Rev2.0)
+ * ---------
+ * <31:28> :: Cable HW version
+ * <27:24> :: Cable FW version
+ * <23:20> :: Reserved, Shall be set to zero
+ * <19:18> :: type-C to Type-A/B/C/Captive (00b == A, 01 == B, 10 == C, 11 == Captive)
+ * <17> :: Reserved, Shall be set to zero
+ * <16:13> :: cable latency (0001 == <10ns(~1m length))
+ * <12:11> :: cable termination type (11b == both ends active VCONN req)
+ * <10> :: SSTX1 Directionality support (0b == fixed, 1b == cfgable)
+ * <9> :: SSTX2 Directionality support
+ * <8> :: SSRX1 Directionality support
+ * <7> :: SSRX2 Directionality support
+ * <6:5> :: Vbus current handling capability (01b == 3A, 10b == 5A)
+ * <4> :: Vbus through cable (0b == no, 1b == yes)
+ * <3> :: SOP" controller present? (0b == no, 1b == yes)
+ * <2:0> :: USB SS Signaling support
+ *
+ * Passive Cable VDO (PD Rev3.0+)
* ---------
* <31:28> :: Cable HW version
* <27:24> :: Cable FW version
@@ -244,7 +266,7 @@
* <4:3> :: Reserved, Shall be set to zero
* <2:0> :: USB highest speed
*
- * Active Cable VDO 1
+ * Active Cable VDO 1 (PD Rev3.0+)
* ---------
* <31:28> :: Cable HW version
* <27:24> :: Cable FW version
@@ -266,7 +288,9 @@
#define CABLE_VDO_VER1_0 0
#define CABLE_VDO_VER1_3 3
-/* Connector Type */
+/* Connector Type (_ATYPE and _BTYPE are for PD Rev2.0 only) */
+#define CABLE_ATYPE 0
+#define CABLE_BTYPE 1
#define CABLE_CTYPE 2
#define CABLE_CAPTIVE 3
@@ -303,12 +327,22 @@
#define CABLE_CURR_3A 1
#define CABLE_CURR_5A 2
+/* USB SuperSpeed Signaling Support (PD Rev2.0) */
+#define CABLE_USBSS_U2_ONLY 0
+#define CABLE_USBSS_U31_GEN1 1
+#define CABLE_USBSS_U31_GEN2 2
+
/* USB Highest Speed */
#define CABLE_USB2_ONLY 0
#define CABLE_USB32_GEN1 1
#define CABLE_USB32_4_GEN2 2
#define CABLE_USB4_GEN3 3
+#define VDO_CABLE(hw, fw, cbl, lat, term, tx1d, tx2d, rx1d, rx2d, cur, vps, sopp, usbss) \
+ (((hw) & 0x7) << 28 | ((fw) & 0x7) << 24 | ((cbl) & 0x3) << 18 \
+ | ((lat) & 0x7) << 13 | ((term) & 0x3) << 11 | (tx1d) << 10 \
+ | (tx2d) << 9 | (rx1d) << 8 | (rx2d) << 7 | ((cur) & 0x3) << 5 \
+ | (vps) << 4 | (sopp) << 3 | ((usbss) & 0x7))
#define VDO_PCABLE(hw, fw, ver, conn, lat, term, vbm, cur, spd) \
(((hw) & 0xf) << 28 | ((fw) & 0xf) << 24 | ((ver) & 0x7) << 21 \
| ((conn) & 0x3) << 18 | ((lat) & 0xf) << 13 | ((term) & 0x3) << 11 \
@@ -374,6 +408,35 @@
| (iso) << 2 | (gen))
/*
+ * AMA VDO (PD Rev2.0)
+ * ---------
+ * <31:28> :: Cable HW version
+ * <27:24> :: Cable FW version
+ * <23:12> :: Reserved, Shall be set to zero
+ * <11> :: SSTX1 Directionality support (0b == fixed, 1b == cfgable)
+ * <10> :: SSTX2 Directionality support
+ * <9> :: SSRX1 Directionality support
+ * <8> :: SSRX2 Directionality support
+ * <7:5> :: Vconn power
+ * <4> :: Vconn power required
+ * <3> :: Vbus power required
+ * <2:0> :: USB SS Signaling support
+ */
+#define VDO_AMA(hw, fw, tx1d, tx2d, rx1d, rx2d, vcpwr, vcr, vbr, usbss) \
+ (((hw) & 0x7) << 28 | ((fw) & 0x7) << 24 \
+ | (tx1d) << 11 | (tx2d) << 10 | (rx1d) << 9 | (rx2d) << 8 \
+ | ((vcpwr) & 0x7) << 5 | (vcr) << 4 | (vbr) << 3 \
+ | ((usbss) & 0x7))
+
+#define PD_VDO_AMA_VCONN_REQ(vdo) (((vdo) >> 4) & 1)
+#define PD_VDO_AMA_VBUS_REQ(vdo) (((vdo) >> 3) & 1)
+
+#define AMA_USBSS_U2_ONLY 0
+#define AMA_USBSS_U31_GEN1 1
+#define AMA_USBSS_U31_GEN2 2
+#define AMA_USBSS_BBONLY 3
+
+/*
* VPD VDO
* ---------
* <31:28> :: HW version
diff --git a/dts/src/riscv/sifive/fu740-c000.dtsi b/dts/src/riscv/sifive/fu740-c000.dtsi
index 8eef82e419..abbb960f90 100644
--- a/dts/src/riscv/sifive/fu740-c000.dtsi
+++ b/dts/src/riscv/sifive/fu740-c000.dtsi
@@ -273,7 +273,7 @@
cache-size = <2097152>;
cache-unified;
interrupt-parent = <&plic0>;
- interrupts = <19 20 21 22>;
+ interrupts = <19 21 22 20>;
reg = <0x0 0x2010000 0x0 0x1000>;
};
gpio: gpio@10060000 {
diff --git a/fs/devfs-core.c b/fs/devfs-core.c
index f804f96974..30ad0e0508 100644
--- a/fs/devfs-core.c
+++ b/fs/devfs-core.c
@@ -529,7 +529,7 @@ void cdev_remove_loop(struct cdev *cdev)
free(cdev);
}
-static ssize_t mem_copy(struct device_d *dev, void *dst, const void *src,
+ssize_t mem_copy(struct device_d *dev, void *dst, const void *src,
resource_size_t count, resource_size_t offset,
unsigned long flags)
{
diff --git a/fs/fs.c b/fs/fs.c
index a9dbf1dd4e..b6431227d6 100644
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -208,9 +208,15 @@ int creat(const char *pathname, mode_t mode)
}
EXPORT_SYMBOL(creat);
+static int fsdev_truncate(struct device_d *dev, FILE *f, loff_t length)
+{
+ struct fs_driver_d *fsdrv = f->fsdev->driver;
+
+ return fsdrv->truncate ? fsdrv->truncate(dev, f, length) : -EROFS;
+}
+
int ftruncate(int fd, loff_t length)
{
- struct fs_driver_d *fsdrv;
FILE *f = fd_to_file(fd);
int ret;
@@ -220,9 +226,7 @@ int ftruncate(int fd, loff_t length)
if (f->size == FILE_SIZE_STREAM)
return 0;
- fsdrv = f->fsdev->driver;
-
- ret = fsdrv->truncate(&f->fsdev->dev, f, length);
+ ret = fsdev_truncate(&f->fsdev->dev, f, length);
if (ret) {
errno = -ret;
return ret;
@@ -332,7 +336,7 @@ static ssize_t __write(FILE *f, const void *buf, size_t count)
assert_command_context();
if (f->size != FILE_SIZE_STREAM && f->pos + count > f->size) {
- ret = fsdrv->truncate(&f->fsdev->dev, f, f->pos + count);
+ ret = fsdev_truncate(&f->fsdev->dev, f, f->pos + count);
if (ret) {
if (ret != -ENOSPC)
goto out;
@@ -2463,7 +2467,7 @@ int open(const char *pathname, int flags, ...)
}
if (flags & O_TRUNC) {
- error = fsdrv->truncate(&fsdev->dev, f, 0);
+ error = fsdev_truncate(&fsdev->dev, f, 0);
f->size = 0;
inode->i_size = 0;
if (error)
diff --git a/include/asm-generic/barebox.lds.h b/include/asm-generic/barebox.lds.h
index 6971e2c1d2..c5f9d97547 100644
--- a/include/asm-generic/barebox.lds.h
+++ b/include/asm-generic/barebox.lds.h
@@ -33,6 +33,7 @@
KEEP(*(.initcall.12)) \
KEEP(*(.initcall.13)) \
KEEP(*(.initcall.14)) \
+ KEEP(*(.initcall.15)) \
__barebox_initcalls_end = .;
#define BAREBOX_EXITCALLS \
@@ -113,6 +114,13 @@
KEEP(*(.rsa_keys.rodata.*)); \
__rsa_keys_end = .; \
+#define BAREBOX_DEEP_PROBE \
+ STRUCT_ALIGN(); \
+ __barebox_deep_probe_start = .; \
+ KEEP(*(SORT_BY_NAME(.barebox_deep_probe*))) \
+ __barebox_deep_probe_end = .;
+
+
#ifdef CONFIG_CONSTRUCTORS
#define KERNEL_CTORS() . = ALIGN(8); \
__ctors_start = .; \
@@ -135,7 +143,8 @@
BAREBOX_CLK_TABLE \
BAREBOX_DTB \
BAREBOX_RSA_KEYS \
- BAREBOX_PCI_FIXUP
+ BAREBOX_PCI_FIXUP \
+ BAREBOX_DEEP_PROBE
#if defined(CONFIG_ARCH_BAREBOX_MAX_BARE_INIT_SIZE) && \
CONFIG_ARCH_BAREBOX_MAX_BARE_INIT_SIZE < CONFIG_BAREBOX_MAX_BARE_INIT_SIZE
diff --git a/include/deep-probe.h b/include/deep-probe.h
new file mode 100644
index 0000000000..f75ad1065c
--- /dev/null
+++ b/include/deep-probe.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __DEEP_PROBE_H
+#define __DEEP_PROBE_H
+
+#include <linux/stringify.h>
+#include <linux/types.h>
+
+struct deep_probe_entry {
+ const struct of_device_id *device_id;
+};
+
+bool deep_probe_is_supported(void);
+
+extern struct deep_probe_entry __barebox_deep_probe_start;
+extern struct deep_probe_entry __barebox_deep_probe_end;
+
+#define __BAREBOX_DEEP_PROBE_ENABLE(_entry,_device_id) \
+ static const struct deep_probe_entry _entry \
+ __attribute__ ((used,section (".barebox_deep_probe_" __stringify(_entry)))) = { \
+ .device_id = _device_id, \
+ }
+
+#define BAREBOX_DEEP_PROBE_ENABLE(_device_id) \
+ __BAREBOX_DEEP_PROBE_ENABLE(__UNIQUE_ID(deepprobe),_device_id)
+
+#endif /* __DEEP_PROBE_H */
diff --git a/include/driver.h b/include/driver.h
index d84fe35d50..c7f5903fce 100644
--- a/include/driver.h
+++ b/include/driver.h
@@ -347,6 +347,9 @@ struct cdev;
/* These are used by drivers which work with direct memory accesses */
ssize_t mem_read(struct cdev *cdev, void *buf, size_t count, loff_t offset, ulong flags);
ssize_t mem_write(struct cdev *cdev, const void *buf, size_t count, loff_t offset, ulong flags);
+ssize_t mem_copy(struct device_d *dev, void *dst, const void *src,
+ resource_size_t count, resource_size_t offset,
+ unsigned long flags);
int generic_memmap_ro(struct cdev *dev, void **map, int flags);
int generic_memmap_rw(struct cdev *dev, void **map, int flags);
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/init.h b/include/init.h
index 2d61bc8963..c695f99867 100644
--- a/include/init.h
+++ b/include/init.h
@@ -47,6 +47,9 @@ typedef void (*exitcall_t)(void);
* initializes variables that couldn't be statically initialized.
*
* This only exists for built-in code, not for modules.
+ *
+ * The only purpose for "of_populate" is to call of_probe() other functions are
+ * not allowed.
*/
#define pure_initcall(fn) __define_initcall("0",fn,0)
@@ -61,9 +64,10 @@ typedef void (*exitcall_t)(void);
#define fs_initcall(fn) __define_initcall("9",fn,9)
#define device_initcall(fn) __define_initcall("10",fn,10)
#define crypto_initcall(fn) __define_initcall("11",fn,11)
-#define late_initcall(fn) __define_initcall("12",fn,12)
-#define environment_initcall(fn) __define_initcall("13",fn,13)
-#define postenvironment_initcall(fn) __define_initcall("14",fn,14)
+#define of_populate_initcall(fn) __define_initcall("12",fn,12)
+#define late_initcall(fn) __define_initcall("13",fn,13)
+#define environment_initcall(fn) __define_initcall("14",fn,14)
+#define postenvironment_initcall(fn) __define_initcall("15",fn,15)
#define early_exitcall(fn) __define_exitcall("0",fn,0)
#define predevshutdown_exitcall(fn) __define_exitcall("1",fn,1)
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/nvmem-consumer.h b/include/linux/nvmem-consumer.h
index 5f44cf00cd..b979f23372 100644
--- a/include/linux/nvmem-consumer.h
+++ b/include/linux/nvmem-consumer.h
@@ -49,6 +49,8 @@ ssize_t nvmem_device_cell_read(struct nvmem_device *nvmem,
int nvmem_device_cell_write(struct nvmem_device *nvmem,
struct nvmem_cell_info *info, void *buf);
+void nvmem_devices_print(void);
+
#else
static inline struct nvmem_cell *nvmem_cell_get(struct device_d *dev,
diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h
index 2d73898373..a293f60c1e 100644
--- a/include/linux/nvmem-provider.h
+++ b/include/linux/nvmem-provider.h
@@ -26,6 +26,7 @@ struct nvmem_config {
struct device_d *dev;
const char *name;
bool read_only;
+ struct cdev *cdev;
int stride;
int word_size;
int size;
@@ -34,11 +35,13 @@ struct nvmem_config {
};
struct regmap;
+struct cdev;
#if IS_ENABLED(CONFIG_NVMEM)
struct nvmem_device *nvmem_register(const struct nvmem_config *cfg);
struct nvmem_device *nvmem_regmap_register(struct regmap *regmap, const char *name);
+struct nvmem_device *nvmem_partition_register(struct cdev *cdev);
#else
@@ -52,5 +55,10 @@ static inline struct nvmem_device *nvmem_regmap_register(struct regmap *regmap,
return ERR_PTR(-ENOSYS);
}
+static inline struct nvmem_device *nvmem_partition_register(struct cdev *cdev)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
#endif /* CONFIG_NVMEM */
#endif /* ifndef _LINUX_NVMEM_PROVIDER_H */
diff --git a/include/linux/reset.h b/include/linux/reset.h
index 0a98ea6bac..818b06412f 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/linux/string.h b/include/linux/string.h
index 55bc905c0e..47a27a391f 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -25,6 +25,10 @@ extern __kernel_size_t strspn(const char *,const char *);
(typeof(&(s1)[0]))(_strstr((s1), (s2))); \
})
+#ifndef __HAVE_ARCH_STRCHRNUL
+extern char * strchrnul(const char *,int);
+#endif
+
/*
* Include machine specific inline routines
*/
diff --git a/include/of.h b/include/of.h
index d67a40bd19..d82790c052 100644
--- a/include/of.h
+++ b/include/of.h
@@ -35,6 +35,7 @@ struct device_node {
struct list_head parent_list;
struct list_head list;
phandle phandle;
+ struct device_d *dev;
};
struct of_device_id {
@@ -109,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;
@@ -162,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);
@@ -267,6 +269,7 @@ extern int barebox_register_fdt(const void *dtb);
extern struct device_d *of_platform_device_create(struct device_node *np,
struct device_d *parent);
+extern void of_platform_device_dummy_drv(struct device_d *dev);
extern int of_platform_populate(struct device_node *root,
const struct of_device_id *matches,
struct device_d *parent);
@@ -276,11 +279,17 @@ extern struct device_d *of_device_enable_and_register_by_name(const char *name);
extern struct device_d *of_device_enable_and_register_by_alias(
const char *alias);
+extern int of_device_ensure_probed(struct device_node *np);
+extern int of_device_ensure_probed_by_alias(const char *alias);
+extern int of_devices_ensure_probed_by_property(const char *property_name);
+extern int of_devices_ensure_probed_by_dev_id(const struct of_device_id *ids);
+
struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node);
int of_parse_partitions(struct cdev *cdev, struct device_node *node);
int of_fixup_partitions(struct device_node *np, struct cdev *cdev);
int of_partitions_register_fixup(struct cdev *cdev);
-int of_device_is_stdout_path(struct device_d *dev);
+struct device_node *of_get_stdoutpath(unsigned int *);
+int of_device_is_stdout_path(struct device_d *dev, unsigned int *baudrate);
const char *of_get_model(void);
void *of_flatten_dtb(struct device_node *node);
int of_add_memory(struct device_node *node, bool dump);
@@ -317,7 +326,12 @@ static inline int of_partitions_register_fixup(struct cdev *cdev)
return -ENOSYS;
}
-static inline int of_device_is_stdout_path(struct device_d *dev)
+static inline struct device_node *of_get_stdoutpath(unsigned int *rate)
+{
+ return NULL;
+}
+
+static inline int of_device_is_stdout_path(struct device_d *dev, unsigned int *baudrate)
{
return 0;
}
@@ -353,6 +367,31 @@ static inline struct device_d *of_platform_device_create(struct device_node *np,
return NULL;
}
+static inline void of_platform_device_dummy_drv(struct device_d *dev)
+{
+}
+
+static inline int of_device_ensure_probed(struct device_node *np)
+{
+ return 0;
+}
+
+static inline int of_device_ensure_probed_by_alias(const char *alias)
+{
+ return 0;
+}
+
+static inline int of_devices_ensure_probed_by_property(const char *property_name)
+{
+ return 0;
+}
+
+static inline int
+of_devices_ensure_probed_by_dev_id(const struct of_device_id *ids)
+{
+ return 0;
+}
+
static inline int of_bus_n_addr_cells(struct device_node *np)
{
return 0;
@@ -1021,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,
@@ -1033,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)
@@ -1047,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;
@@ -1061,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/include/of_net.h b/include/of_net.h
index f37af58303..4e23deb28e 100644
--- a/include/of_net.h
+++ b/include/of_net.h
@@ -6,8 +6,27 @@
#ifndef __LINUX_OF_NET_H
#define __LINUX_OF_NET_H
+#include <linux/types.h>
#include <of.h>
+
+#ifdef CONFIG_OFTREE
+int of_get_mac_addr_nvmem(struct device_node *np, u8 *addr);
+int of_get_mac_address(struct device_node *np, u8 *addr);
int of_get_phy_mode(struct device_node *np);
-const void *of_get_mac_address(struct device_node *np);
+#else
+static inline int of_get_mac_addr_nvmem(struct device_node *np, u8 *addr)
+{
+ return -ENOSYS;
+}
+static inline int of_get_mac_address(struct device_node *np, u8 *addr)
+{
+ return -ENOSYS;
+}
+
+static inline int of_get_phy_mode(struct device_node *np)
+{
+ return -ENOSYS;
+}
+#endif
#endif /* __LINUX_OF_NET_H */
diff --git a/lib/Kconfig b/lib/Kconfig
index 922710e106..ea6de76a22 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -155,7 +155,7 @@ source "lib/logo/Kconfig"
source "lib/bootstrap/Kconfig"
config PROGRESS_NOTIFIER
- bool
+ bool "Progress Notifier" if COMPILE_TEST
help
This is selected by boards that register a notifier to visualize
progress, like blinking a LED during an update.
diff --git a/lib/image-sparse.c b/lib/image-sparse.c
index c375c78d63..eb5242e25a 100644
--- a/lib/image-sparse.c
+++ b/lib/image-sparse.c
@@ -46,10 +46,6 @@
#include <linux/math64.h>
-#ifndef CONFIG_FASTBOOT_FLASH_FILLBUF_SIZE
-#define CONFIG_FASTBOOT_FLASH_FILLBUF_SIZE (1024 * 512)
-#endif
-
struct sparse_image_ctx {
int fd;
struct sparse_header sparse;
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
*
diff --git a/lib/string.c b/lib/string.c
index bad186586f..9aeb1d8830 100644
--- a/lib/string.c
+++ b/lib/string.c
@@ -274,6 +274,24 @@ char * _strchr(const char * s, int c)
#endif
EXPORT_SYMBOL(_strchr);
+#ifndef __HAVE_ARCH_STRCHRNUL
+/**
+ * strchrnul - Find and return a character in a string, or end of string
+ * @s: The string to be searched
+ * @c: The character to search for
+ *
+ * Returns pointer to first occurrence of 'c' in s. If c is not found, then
+ * return a pointer to the null byte at the end of s.
+ */
+char *strchrnul(const char *s, int c)
+{
+ while (*s && *s != (char)c)
+ s++;
+ return (char *)s;
+}
+EXPORT_SYMBOL(strchrnul);
+#endif
+
#ifndef __HAVE_ARCH_STRRCHR
/**
* strrchr - Find the last occurrence of a character in a string
diff --git a/net/eth.c b/net/eth.c
index 84f99d3aa8..762c5dfb8a 100644
--- a/net/eth.c
+++ b/net/eth.c
@@ -11,6 +11,7 @@
#include <net.h>
#include <dma.h>
#include <of.h>
+#include <of_net.h>
#include <linux/phy.h>
#include <errno.h>
#include <malloc.h>
@@ -504,3 +505,26 @@ void led_trigger_network(enum led_trigger trigger)
led_trigger(trigger, TRIGGER_FLASH);
led_trigger(LED_TRIGGER_NET_TXRX, TRIGGER_FLASH);
}
+
+static int of_populate_ethaddr(void)
+{
+ char str[sizeof("xx:xx:xx:xx:xx:xx")];
+ struct eth_device *edev;
+ int ret;
+
+ list_for_each_entry(edev, &netdev_list, list) {
+ if (!edev->parent || is_valid_ether_addr(edev->ethaddr))
+ continue;
+
+ ret = of_get_mac_addr_nvmem(edev->parent->device_node, edev->ethaddr);
+ if (ret)
+ continue;
+
+ ethaddr_to_string(edev->ethaddr, str);
+ dev_info(&edev->dev, "Got preset MAC address from device tree: %s\n", str);
+ eth_set_ethaddr(edev, edev->ethaddr);
+ }
+
+ return 0;
+}
+postenvironment_initcall(of_populate_ethaddr);
diff --git a/test/kconfig/base.cfg b/test/kconfig/base.cfg
index 6a9f683498..80b9c68f02 100644
--- a/test/kconfig/base.cfg
+++ b/test/kconfig/base.cfg
@@ -1,3 +1,4 @@
+CONFIG_COMPILE_TEST=y
CONFIG_TEST=y
CONFIG_SELFTEST=y
CONFIG_CMD_SELFTEST=y
diff --git a/test/kconfig/full.cfg b/test/kconfig/full.cfg
index 39275768ea..547100bacc 100644
--- a/test/kconfig/full.cfg
+++ b/test/kconfig/full.cfg
@@ -1,2 +1,3 @@
CONFIG_BTHREAD=y
CONFIG_CMD_BTHREAD=y
+CONFIG_PROGRESS_NOTIFIER=y
diff --git a/test/self/Kconfig b/test/self/Kconfig
index 73dc6c7b4f..dfaa32dda0 100644
--- a/test/self/Kconfig
+++ b/test/self/Kconfig
@@ -28,6 +28,7 @@ config SELFTEST_AUTORUN
config SELFTEST_ENABLE_ALL
bool "Enable all self-tests"
select SELFTEST_PRINTF
+ select SELFTEST_PROGRESS_NOTIFIER
help
Selects all self-tests compatible with current configuration
@@ -36,4 +37,7 @@ config SELFTEST_PRINTF
help
Tests barebox vsnprintf() functionality
+config SELFTEST_PROGRESS_NOTIFIER
+ bool "progress notifier selftest"
+
endif
diff --git a/test/self/Makefile b/test/self/Makefile
index b4aa49d6f8..e78ccc3cfb 100644
--- a/test/self/Makefile
+++ b/test/self/Makefile
@@ -2,3 +2,4 @@
obj-$(CONFIG_SELFTEST) += core.o
obj-$(CONFIG_SELFTEST_PRINTF) += printf.o
+obj-$(CONFIG_SELFTEST_PROGRESS_NOTIFIER) += progress-notifier.o
diff --git a/test/self/progress-notifier.c b/test/self/progress-notifier.c
new file mode 100644
index 0000000000..af65b0900e
--- /dev/null
+++ b/test/self/progress-notifier.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <common.h>
+#include <bselftest.h>
+#include <progress.h>
+
+BSELFTEST_GLOBALS();
+
+static void __ok(bool cond, const char *func, int line)
+{
+ total_tests++;
+ if (!cond) {
+ failed_tests++;
+ printf("%s:%d: assertion failure\n", func, line);
+ }
+}
+
+#define ok(cond) \
+ __ok(cond, __func__, __LINE__)
+
+static unsigned long stage;
+static const void *prefix;
+static int counter;
+
+static int dummy_notifier(struct notifier_block *r, unsigned long _stage, void *_prefix)
+{
+ prefix = _prefix;
+ stage = _stage;
+ counter++;
+ return 0;
+}
+
+static struct notifier_block dummy_nb = {
+ .notifier_call = dummy_notifier
+};
+
+static void test_dummy_notifier(void)
+{
+ const char *arg = "ARGUMENT";
+ int local_counter = 0;
+
+ stage = 0;
+ prefix = NULL;
+ counter = 0;
+
+ progress_register_client(&dummy_nb);
+ ok(stage == 0);
+ ok(prefix == NULL);
+ ok(counter == local_counter);
+ progress_notifier_call_chain(1, arg);
+
+ if (IS_ENABLED(CONFIG_PROGRESS_NOTIFIER)) {
+ ok(stage == 1);
+ ok(prefix == arg);
+ ok(counter == ++local_counter);
+ progress_notifier_call_chain(0, NULL);
+ local_counter++;
+ } else {
+ total_tests += 2;
+ skipped_tests += 2;
+ }
+
+ ok(stage == 0);
+ ok(prefix == NULL || *(const char *)prefix == '\0');
+ ok(counter == local_counter);
+ progress_unregister_client(&dummy_nb);
+
+ ok(stage == 0);
+ ok(prefix == NULL || *(const char *)prefix == '\0');
+ ok(counter == local_counter);
+}
+
+static void test_notifier(void)
+{
+ test_dummy_notifier();
+}
+bselftest(core, test_notifier);