diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2021-07-18 07:13:26 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2021-07-18 07:13:26 +0200 |
commit | ca922d6044e49b1ed9782aa8eb28d1ed70931978 (patch) | |
tree | 6cac7b356b9fc9f4c152eafca0e38d14c311577b | |
parent | 889612fab092ec20463c886513b5cfde14cc8cb0 (diff) | |
parent | 5bf59d3f2baa0f98ab93ddb4ea8a3b37986db608 (diff) | |
download | barebox-ca922d6044e49b1ed9782aa8eb28d1ed70931978.tar.gz |
Merge branch 'for-next/nvmem'
34 files changed, 548 insertions, 134 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 @@ -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/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 5ae3cb3dd1..7bb36d6e41 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/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/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index e4a72b1431..0d7c0b7b9e 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 617e3725a7..53c02dc785 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 8f4b4646a9..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,12 +214,12 @@ 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; @@ -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/base.c b/drivers/of/base.c index 0ae9a845a4..42b8d24874 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -2354,16 +2354,29 @@ 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; + struct device_node *node; if(!root_node) return -ENODEV; - firmware = of_find_node_by_path("/firmware"); - if (firmware) - of_platform_populate(firmware, NULL, NULL); + /* + * 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); + + node = of_find_node_by_path("/firmware"); + if (node) + of_platform_populate(node, NULL, NULL); of_platform_device_create_root(root_node); 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/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/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/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/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/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/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 */ @@ -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); |