From ebb73de1687cfd6449f492b54cc2f32b4b0ce9c5 Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Tue, 27 Nov 2018 23:00:18 +0100 Subject: [PATCH] bootcount: add uclass for bootcount The original bootcount methods do not provide an interface to DM and rely on a static configuration for I2C devices (e.g. bus, chip-addr, etc. are configured through defines statically). On a modern system that exposes multiple devices in a DTS-configurable way, this is less than optimal and a interface to DM-based devices will be desirable. This adds a simple driver that is DM-aware and configurable via DTS. If ambiguous (i.e. multiple bootcount-devices are present) the /chosen/u-boot,bootcount-device property can be used to select one bootcount device. Initially, this provides support for the following DM devices: * RTC devices Signed-off-by: Philipp Tomsich Tested-by: Klaus Goger --- doc/device-tree-bindings/chosen.txt | 30 +++++++++ drivers/bootcount/Kconfig | 8 +++ drivers/bootcount/Makefile | 2 + drivers/bootcount/bootcount-uclass.c | 93 ++++++++++++++++++++++++++++ include/bootcount.h | 48 ++++++++++++++ include/dm/uclass-id.h | 1 + 6 files changed, 182 insertions(+) create mode 100644 drivers/bootcount/bootcount-uclass.c diff --git a/doc/device-tree-bindings/chosen.txt b/doc/device-tree-bindings/chosen.txt index 86c533ad6d..395c9501e3 100644 --- a/doc/device-tree-bindings/chosen.txt +++ b/doc/device-tree-bindings/chosen.txt @@ -42,6 +42,36 @@ Example }; }; +u-boot,bootcount-device property +-------------------------------- + +In a DM-based system, the bootcount may be stored in a device known to +the DM framework (e.g. in a battery-backed SRAM area within a RTC +device) managed by a device conforming to UCLASS_BOOTCOUNT. If +multiple such devices are present in a system concurrently, then the +u-boot,bootcount-device property can select the preferred target. + +Example +------- +/ { + chosen { + u-boot,bootcount-device = &bootcount-rv3029; + }; + + bootcount-rv3029: bootcount@0 { + compatible = "u-boot,bootcount-rtc"; + rtc = &rv3029; + offset = <0x38>; + }; + + i2c2 { + rv3029: rtc@56 { + compatible = "mc,rv3029"; + reg = <0x56>; + }; + }; +}; + u-boot,spl-boot-order property ------------------------------ diff --git a/drivers/bootcount/Kconfig b/drivers/bootcount/Kconfig index 67033637c0..46571eb322 100644 --- a/drivers/bootcount/Kconfig +++ b/drivers/bootcount/Kconfig @@ -70,6 +70,14 @@ config BOOTCOUNT_AT91 bool "Boot counter for Atmel AT91SAM9XE" depends on AT91SAM9XE +config DM_BOOTCOUNT + bool "Boot counter in a device-model device" + help + Enables reading/writing the bootcount in a device-model based + backing store. If an entry in /chosen/u-boot,bootcount-device + exists, this will be the preferred bootcount device; otherwise + the first available bootcount device will be used. + endchoice config BOOTCOUNT_BOOTLIMIT diff --git a/drivers/bootcount/Makefile b/drivers/bootcount/Makefile index 68bc006b75..81980b3cad 100644 --- a/drivers/bootcount/Makefile +++ b/drivers/bootcount/Makefile @@ -7,3 +7,5 @@ obj-$(CONFIG_BOOTCOUNT_RAM) += bootcount_ram.o obj-$(CONFIG_BOOTCOUNT_ENV) += bootcount_env.o obj-$(CONFIG_BOOTCOUNT_I2C) += bootcount_i2c.o obj-$(CONFIG_BOOTCOUNT_EXT) += bootcount_ext.o + +obj-$(CONFIG_DM_BOOTCOUNT) += bootcount-uclass.o diff --git a/drivers/bootcount/bootcount-uclass.c b/drivers/bootcount/bootcount-uclass.c new file mode 100644 index 0000000000..0689db7a5b --- /dev/null +++ b/drivers/bootcount/bootcount-uclass.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2018 Theobroma Systems Design und Consulting GmbH + */ + +#include +#include +#include +#include + +int dm_bootcount_get(struct udevice *dev, u32 *bootcount) +{ + struct bootcount_ops *ops = bootcount_get_ops(dev); + + assert(ops); + if (!ops->get) + return -ENOSYS; + return ops->get(dev, bootcount); +} + +int dm_bootcount_set(struct udevice *dev, const u32 bootcount) +{ + struct bootcount_ops *ops = bootcount_get_ops(dev); + + assert(ops); + if (!ops->set) + return -ENOSYS; + return ops->set(dev, bootcount); +} + +/* Now implement the generic default functions */ +void bootcount_store(ulong val) +{ + struct udevice *dev = NULL; + ofnode node; + const char *propname = "u-boot,bootcount-device"; + int ret = -ENODEV; + + /* + * If there's a preferred bootcount device selected by the user (by + * setting '/chosen/u-boot,bootcount-device' in the DTS), try to use + * it if available. + */ + node = ofnode_get_chosen_node(propname); + if (ofnode_valid(node)) + ret = uclass_get_device_by_ofnode(UCLASS_BOOTCOUNT, node, &dev); + + /* If there was no user-selected device, use the first available one */ + if (ret) + ret = uclass_get_device(UCLASS_BOOTCOUNT, 0, &dev); + + if (dev) + ret = dm_bootcount_set(dev, val); + + if (ret) + pr_debug("%s: failed to store 0x%lx\n", __func__, val); +} + +ulong bootcount_load(void) +{ + struct udevice *dev = NULL; + ofnode node; + const char *propname = "u-boot,bootcount-device"; + int ret = -ENODEV; + u32 val; + + /* + * If there's a preferred bootcount device selected by the user (by + * setting '/chosen/u-boot,bootcount-device' in the DTS), try to use + * it if available. + */ + node = ofnode_get_chosen_node(propname); + if (ofnode_valid(node)) + ret = uclass_get_device_by_ofnode(UCLASS_BOOTCOUNT, node, &dev); + + /* If there was no user-selected device, use the first available one */ + if (ret) + ret = uclass_get_device(UCLASS_BOOTCOUNT, 0, &dev); + + if (dev) + ret = dm_bootcount_get(dev, &val); + + if (ret) + pr_debug("%s: failed to load bootcount\n", __func__); + + /* Return the 0, if the call to dm_bootcount_get failed */ + return ret ? 0 : val; +} + +UCLASS_DRIVER(bootcount) = { + .name = "bootcount", + .id = UCLASS_BOOTCOUNT, +}; diff --git a/include/bootcount.h b/include/bootcount.h index 671adcc410..daee84316c 100644 --- a/include/bootcount.h +++ b/include/bootcount.h @@ -10,6 +10,54 @@ #include #include +#ifdef CONFIG_DM_BOOTCOUNT + +struct bootcount_ops { + /** + * get() - get the current bootcount value + * + * Returns the current counter value of the bootcount backing + * store. + * + * @dev: Device to read from + * @bootcount: Address to put the current bootcount value + */ + int (*get)(struct udevice *dev, u32 *bootcount); + + /** + * set() - set a bootcount value (e.g. to reset or increment) + * + * Sets the value in the bootcount backing store. + * + * @dev: Device to read from + * @bootcount: New bootcount value to store + */ + int (*set)(struct udevice *dev, const u32 bootcount); +}; + +/* Access the operations for a bootcount device */ +#define bootcount_get_ops(dev) ((struct bootcount_ops *)(dev)->driver->ops) + +/** + * dm_bootcount_get() - Read the current value from a bootcount storage + * + * @dev: Device to read from + * @bootcount: Place to put the current bootcount + * @return 0 if OK, -ve on error + */ +int dm_bootcount_get(struct udevice *dev, u32 *bootcount); + +/** + * dm_bootcount_set() - Write a value to a bootcount storage + * + * @dev: Device to read from + * @bootcount: Value to be written to the backing storage + * @return 0 if OK, -ve on error + */ +int dm_bootcount_set(struct udevice *dev, u32 bootcount); + +#endif + #if defined(CONFIG_SPL_BOOTCOUNT_LIMIT) || defined(CONFIG_BOOTCOUNT_LIMIT) #if !defined(CONFIG_SYS_BOOTCOUNT_LE) && !defined(CONFIG_SYS_BOOTCOUNT_BE) diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 037af0460c..bbe842e59a 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -32,6 +32,7 @@ enum uclass_id { UCLASS_AXI, /* AXI bus */ UCLASS_BLK, /* Block device */ UCLASS_BOARD, /* Device information from hardware */ + UCLASS_BOOTCOUNT, /* Bootcount backing store */ UCLASS_CLK, /* Clock source, e.g. used by peripherals */ UCLASS_CPU, /* CPU, typically part of an SoC */ UCLASS_CROS_EC, /* Chrome OS EC */