Bootcount driver using NVMEM cell as backend.
Subsystems (2)
v2 cover Bootcount driver using NVMEM cell as backend.
Description
-----------
Implement a bootcount (1) related driver that uses NVMEM as a backend. The patchset will also update the snvs_lpgpr driver to support 2 bytes NVMEM cells.
1. https://www.denx.de/wiki/view/DULG/UBootBootCountLimit
Testing
-------
Hardware: i.MX6sx evaluation board
Kernel: linux-imx-5.4.24
1. Configure the bootcount hardware in DT to use a NVMEM cell provided by snvs_lpgpr driver as backend.
e.g. This will configure a 4 bytes long NVMEM cell
```
bootcount_regs: bootcount-snvs-regs@0 {
reg = <0x0 0x04>;
};
```2. Record the current NVMEM cell content:
``` ~ # hexdump -C /sys/devices/soc0/soc/2000000.aips-bus/20cc000.snvs/20cc000.snvs:snvs-lpgpr/20cc000.snvs:snvs-lpgpr0/nvmem 00000000 00 00 01 b0 |....| 00000004 ```
3. Write the bootcount and check that is successful: PASS
``` ~ # echo 1 > /sys/bus/platform/drivers/bootcount-nvmem/bootcount/value bootcount: Write regval: 0xb0010001 ~ # hexdump -C /sys/devices/soc0/soc/2000000.aips-bus/20cc000.snvs/20cc000.snvs:snvs-lpgpr/20cc000.snvs:snvs-lpgpr0/nvmem 00000000 01 00 01 b0 |....| 00000004 ```
Note: similar tests were done also for 2 bytes NVMEM cell size.
Kernel: linux-next, tag: next-20210322
1. Enable bootcount and snvs_lpgpr drivers
2. Verify that they compile succesfully: PASS
``` kernel-master> make -j2 drivers/nvmem/ DESCEND objtool CALL scripts/atomic/check-atomics.sh CC arch/x86/kernel/asm-offsets.s CALL scripts/checksyscalls.sh CC drivers/nvmem/core.o CC drivers/nvmem/bootcount-nvmem.o CC drivers/nvmem/snvs_lpgpr.o AR drivers/nvmem/built-in. ```
Testing the bootcount YAML document:
1. Check the document by running the command:make DT_CHECKER_FLAGS=-m dt_binding_check
2. Verify that is successful: PASS
Changes since v1
----------------
- Fix the dt-bindings YAML document.
Nandor Han (4):
dt-bindings: nvmem: Add bootcount-nvmem
nvmem: bootcount: add bootcount driver
nvmem: snvs_lpgpr: use cell stride for regmap size calculation
nvmem: snvs_lpgpr: support two bytes NVMEM cell size
.../bindings/nvmem/bootcount-nvmem.yaml | 66 ++++++ drivers/nvmem/Kconfig | 10 + drivers/nvmem/Makefile | 1 + drivers/nvmem/bootcount-nvmem.c | 195 ++++++++++++++++++ drivers/nvmem/snvs_lpgpr.c | 67 +++++- 5 files changed, 333 insertions(+), 6 deletions(-) create mode 100644 Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml create mode 100644 drivers/nvmem/bootcount-nvmem.c
Signature · 1 line
2.26.3
PATCH v2 1/4 dt-bindings: nvmem: Add bootcount-nvmem
Documents the device tree bindings for bootcount-nvmem driver.
--- .../bindings/nvmem/bootcount-nvmem.yaml | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml
Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml new file +66 -0
@@ -0,0 +1,66 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +# Copyright (c) Vaisala Oyj. All rights reserved. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/nvmem/bootcount-nvmem.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Bootcount NVMEM bindings + +maintainers: + - Nandor Han <[email protected]> + +description: | + This binding is intendent to describe the hardware location for + storing the bootcount value and magic combo. + + The NVMEM cell size should be 2 or 4 bytes. + +allOf: + - $ref: "nvmem-consumer.yaml#" + +properties: + compatible: + enum: + - linux,bootcount-nvmem + + nvmem-cells: + description: Phandle to reboot mode nvmem data cell. + $ref: /schemas/types.yaml#/definitions/phandle-array + + nvmem-cell-names: + description: Name of the NVMEM cell. + $ref: /schemas/types.yaml#/definitions/string-array + enum: + - bootcount-regs + + linux,bootcount-magic: + description: Override default mask value. + $ref: /schemas/types.yaml#/definitions/uint32 + +dependencies: + nvmem-cell-names: [ nvmem-cells ] + +required: + - compatible + - nvmem-cells + - nvmem-cell-names + +additionalProperties: false + +examples: + # example with 16 bit nvram cell: + - | + bootcount { + compatible = "linux,bootcount-nvmem"; + nvmem-cells = <&bootcount_regs>; + nvmem-cell-names = "bootcount-regs"; + }; + + rtc: rtc@68 { + bootcount_regs: bootcount_nvmem_regs@e { + reg = <0x0e 0x2>; + }; + }; + +...
Signature · 1 line
2.26.3
Rob Herring
maintainerRe: [PATCH v2 1/4] dt-bindings: nvmem: Add bootcount-nvmem
Quoted 8 lines · Nandor Han
Documents the device tree bindings for
bootcount-nvmemdriver.Signed-off-by Nandor Han <[email protected]>--- .../bindings/nvmem/bootcount-nvmem.yaml | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml
My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check' on your patch (DT_CHECKER_FLAGS is new in v5.13):
yamllint warnings/errors:
dtschema/dtc warnings/errors:
Documentation/devicetree/bindings/nvmem/bootcount-nvmem.example.dts:27.17-34: Warning (reg_format): /example-0/rtc@68/bootcount_nvmem_regs@e:reg: property has invalid length (8 bytes) (#address-cells == 2, #size-cells == 1) Documentation/devicetree/bindings/nvmem/bootcount-nvmem.example.dts:25.21-29.11: Warning (unit_address_vs_reg): /example-0/rtc@68: node has a unit name, but no reg or ranges property Documentation/devicetree/bindings/nvmem/bootcount-nvmem.example.dt.yaml: Warning (pci_device_reg): Failed prerequisite 'reg_format' Documentation/devicetree/bindings/nvmem/bootcount-nvmem.example.dt.yaml: Warning (pci_device_bus_num): Failed prerequisite 'reg_format' Documentation/devicetree/bindings/nvmem/bootcount-nvmem.example.dt.yaml: Warning (simple_bus_reg): Failed prerequisite 'reg_format' Documentation/devicetree/bindings/nvmem/bootcount-nvmem.example.dt.yaml: Warning (i2c_bus_reg): Failed prerequisite 'reg_format' Documentation/devicetree/bindings/nvmem/bootcount-nvmem.example.dt.yaml: Warning (spi_bus_reg): Failed prerequisite 'reg_format' Documentation/devicetree/bindings/nvmem/bootcount-nvmem.example.dts:26.52-28.15: Warning (avoid_default_addr_size): /example-0/rtc@68/bootcount_nvmem_regs@e: Relying on default #address-cells value Documentation/devicetree/bindings/nvmem/bootcount-nvmem.example.dts:26.52-28.15: Warning (avoid_default_addr_size): /example-0/rtc@68/bootcount_nvmem_regs@e: Relying on default #size-cells value Documentation/devicetree/bindings/nvmem/bootcount-nvmem.example.dt.yaml: Warning (unique_unit_address): Failed prerequisite 'avoid_default_addr_size'
See https://patchwork.ozlabs.org/patch/1471179
This check can fail if there are any dependencies. The base for a patch series is generally the most recent rc1.
If you already ran 'make dt_binding_check' and didn't see the above error(s), then make sure 'yamllint' is installed and dt-schema is up to date:
pip3 install dtschema --upgrade
Please check and re-submit.
Rob Herring
maintainerRe: [PATCH v2 1/4] dt-bindings: nvmem: Add bootcount-nvmem
In reply to Nandor HanQuoted 29 lines · Nandor Han
Documents the device tree bindings for
bootcount-nvmemdriver.Signed-off-by Nandor Han <[email protected]>--- .../bindings/nvmem/bootcount-nvmem.yaml | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yamlDocumentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml new file +15
-0@@ -0,0 +1,66 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +# Copyright (c) Vaisala Oyj. All rights reserved. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/nvmem/bootcount-nvmem.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Bootcount NVMEM bindings + +maintainers: + - Nandor Han <[email protected]> + +description: | + This binding is intendent to describe the hardware location for + storing the bootcount value and magic combo.
Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml line 25
Quoted 10 lines
Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml new file +10
-0+ + The NVMEM cell size should be 2 or 4 bytes. + +allOf: + - $ref: "nvmem-consumer.yaml#" + +properties: + compatible: + enum: + - linux,bootcount-nvmem
What makes this Linux specific? IIRC, u-boot has boot counting function too.
Quoted 4 lines
Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml new file +4
-0+ + nvmem-cells: + description: Phandle to reboot mode nvmem data cell. + $ref: /schemas/types.yaml#/definitions/phandle-array
Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml line 39
Quoted 10 lines
Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml new file +10
-0+ + nvmem-cell-names: + description: Name of the NVMEM cell. + $ref: /schemas/types.yaml#/definitions/string-array + enum: + - bootcount-regs + + linux,bootcount-magic: + description: Override default mask value. + $ref: /schemas/types.yaml#/definitions/uint32
I don't understand what this is. Is it magic or a mask?
Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml line 42
Quoted 3 lines
Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml new file +3
-0+ +dependencies: + nvmem-cell-names: [ nvmem-cells ]
Core schema takes care of this.
Quoted 10 lines
Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml new file +10
-0+ +required: + - compatible + - nvmem-cells + - nvmem-cell-names + +additionalProperties: false + +examples: + # example with 16 bit nvram cell:
Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml line 62
Quoted 10 lines
Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml new file +10
-0+ - | + bootcount { + compatible = "linux,bootcount-nvmem"; + nvmem-cells = <&bootcount_regs>; + nvmem-cell-names = "bootcount-regs"; + }; + + rtc: rtc@68 { + bootcount_regs: bootcount_nvmem_regs@e { + reg = <0x0e 0x2>;
It would be simpler to just add a compatible here and get rid of the 'bootcount' node here.
Quoted 7 lines
Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml new file +4
-0+ }; + }; + +...Signature · 1 line
2.26.3
Re: [PATCH v2 1/4] dt-bindings: nvmem: Add bootcount-nvmem
Hi,
Thanks for your feedback.
<snip>
Quoted 9 lines
Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml line 25
Quoted 5 lines
Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml new file +5
-0+ +properties: + compatible: + enum: + - linux,bootcount-nvmemWhat makes this Linux specific? IIRC, u-boot has boot counting function too.
U-Boot has indeed the counterpart functionality of bootcount feature, however, in this particularly case is not called u-boot,bootcount-nvmem. If you have any suggestions I'm happy to change it.
Should I remove the linux prefix?
<snip>
Quoted 6 lines
Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml line 39
Quoted 4 lines
Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml new file +4
-0+ + linux,bootcount-magic: + description: Override default mask value. + $ref: /schemas/types.yaml#/definitions/uint32I don't understand what this is. Is it magic or a mask?
It's the magic value. Seems to be a mistake in the description. I'll correct this.
Quoted 7 lines
Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml line 42
Quoted 4 lines
Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml new file +3
-0+ +dependencies: + nvmem-cell-names: [ nvmem-cells ]Core schema takes care of this.
Ok. I will remove it in that case.
<snip>
Quoted 7 lines
Quoted 7 lines
Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml new file +7
-0+examples: + # example with 16 bit nvram cell: + - | + bootcount { + compatible = "linux,bootcount-nvmem"; + nvmem-cells = <&bootcount_regs>; + nvmem-cell-names = "bootcount-regs";
Quoted 9 lines
Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml line 62
Quoted 5 lines
Documentation/devicetree/bindings/nvmem/bootcount-nvmem.yaml new file +5
-0+ }; + + rtc: rtc@68 { + bootcount_regs: bootcount_nvmem_regs@e { + reg = <0x0e 0x2>;It would be simpler to just add a compatible here and get rid of the 'bootcount' node here.
This is the configuration for NVMEM cell provider and is done according to bindings/nvmem.yaml document. Is here something that I'm missing?
At least this method of declaring NVMEM cells providers are decoupling the code from hardware declaration resulting in a more robust code.
Regards,
Nandor
PATCH v2 2/4 nvmem: bootcount: add bootcount driver
In reply to Nandor HanIn order to have a robust system we want to be able to identify and take actions if a boot loop occurs. This is possible by using the bootcount feature, which can be used to identify the number of times device has booted since bootcount was last time reset. Bootcount feature (1) requires a collaboration between bootloader and user-space, where the bootloader will increase a counter and user-space reset it. If the counter is not reset and a pre-established threshold is reached, bootloader can react and take action.
This is the kernel side implementation, which can be used to identify the number of times device has booted since bootcount was last time reset.
The driver supports both 16 and 32 bits NVMEM cell size.
1) https://www.denx.de/wiki/DULG/UBootBootCountLimit
--- drivers/nvmem/Kconfig | 10 ++ drivers/nvmem/Makefile | 1 + drivers/nvmem/bootcount-nvmem.c | 195 ++++++++++++++++++++++++++++++++ 3 files changed, 206 insertions(+) create mode 100644 drivers/nvmem/bootcount-nvmem.c
drivers/nvmem/Kconfig +10 -0
@@ -288,4 +288,14 @@ config NVMEM_BRCM_NVRAM This driver provides support for Broadcom's NVRAM that can be accessed using I/O mapping. +config BOOTCOUNT_NVMEM + bool "Bootcount driver using nvmem registers" + depends on OF + depends on NVMEM + help + Driver that implements the bootcount feature support using a + NVMEM cell as a backend. The driver supports 2 and 4 bytes + size cells. + + Say y here to enable bootcount support. endif
drivers/nvmem/Makefile +1 -0
@@ -59,3 +59,4 @@ obj-$(CONFIG_NVMEM_RMEM) += nvmem-rmem.o nvmem-rmem-y := rmem.o obj-$(CONFIG_NVMEM_BRCM_NVRAM) += nvmem_brcm_nvram.o nvmem_brcm_nvram-y := brcm_nvram.o +obj-$(CONFIG_BOOTCOUNT_NVMEM) += bootcount-nvmem.o
drivers/nvmem/bootcount-nvmem.c new file +195 -0
195 lines not on this page · raw message
Signature · 1 line
2.26.3
PATCH v2 3/4 nvmem: snvs_lpgpr: use cell stride for regmap size calculation
In reply to Nandor HanUsing a hard-coded value for calculating the number of registers to read makes future changes more challenging.
Change the calculation to use the NVMEM cell stride instead of a hard coded value. This will allow specifying different NVMEM cell sizes.
--- drivers/nvmem/snvs_lpgpr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/nvmem/snvs_lpgpr.c +3 -3
@@ -72,7 +72,7 @@ static int snvs_lpgpr_write(void *context, unsigned int offset, void *val,return -EPERM ;return regmap_bulk_write (priv->regmap, dcfg->offset + offset, val, - bytes /4 ); + bytes / priv->cfg.stride); } static int snvs_lpgpr_read(void *context, unsigned int offset, void *val,
@@ -81,8 +81,8 @@ static int snvs_lpgpr_read(void *context, unsigned int offset, void *val,struct snvs_lpgpr_priv *priv = context;const struct snvs_lpgpr_cfg *dcfg = priv->dcfg; -return regmap_bulk_read (priv->regmap, dcfg->offset + offset, - val, bytes /4 ); +return regmap_bulk_read (priv->regmap, dcfg->offset + offset, val, + bytes / priv->cfg.stride); }static int snvs_lpgpr_probe (struct platform_device *pdev)
Signature · 1 line
2.26.3
PATCH v2 4/4 nvmem: snvs_lpgpr: support two bytes NVMEM cell size
In reply to Nandor HanIn some situation is desired to use less than 4 bytes for storing data. This will allow using the same register for multiple purposes.
Add support for allowing 2 bytes granularity for NVMEM cells.
--- drivers/nvmem/snvs_lpgpr.c | 67 ++++++++++++++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 6 deletions(-)
drivers/nvmem/snvs_lpgpr.c +61 -6
@@ -21,6 +21,9 @@#define IMX_GPR_SL BIT(5)#define IMX_GPR_HL BIT(5) +#define REGMAP_FIELD_SIZE 16 +#define REGMAP_FIELDS_PER_REG 2 +struct snvs_lpgpr_cfg {int offset;int offset_hplr;
@@ -33,6 +36,7 @@ struct snvs_lpgpr_priv {struct regmap *regmap;struct nvmem_config cfg;const struct snvs_lpgpr_cfg *dcfg; +struct regmap_field **reg_fields; };static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx6q = {
@@ -56,6 +60,11 @@ static int snvs_lpgpr_write(void *context, unsigned int offset, void *val,const struct snvs_lpgpr_cfg *dcfg = priv->dcfg;unsigned int lock_reg;int ret; +u32 regval; +unsigned int field_id; + +if (offset + bytes > dcfg->size) +return -EINVAL ; ret =regmap_read (priv->regmap, dcfg->offset_hplr, &lock_reg);if (ret <0 )
@@ -71,8 +80,16 @@ static int snvs_lpgpr_write(void *context, unsigned int offset, void *val,if (lock_reg &IMX_GPR_HL )return -EPERM ; -return regmap_bulk_write (priv->regmap, dcfg->offset + offset, val, - bytes / priv->cfg.stride); +if (bytes == (REGMAP_FIELD_SIZE >>3 )) { + regval = *(u16 *)(val); + field_id = offset /REGMAP_FIELDS_PER_REG ; + ret =regmap_field_write (priv->reg_fields[field_id], regval); + }else { + ret =regmap_bulk_write (priv->regmap, dcfg->offset + offset, + val, bytes / priv->cfg.stride); + } + +return ret; } static int snvs_lpgpr_read(void *context, unsigned int offset, void *val,
@@ -80,9 +97,27 @@ static int snvs_lpgpr_read(void *context, unsigned int offset, void *val, {struct snvs_lpgpr_priv *priv = context;const struct snvs_lpgpr_cfg *dcfg = priv->dcfg; +int ret; +u32 regval; +unsigned int field_id; -return regmap_bulk_read (priv->regmap, dcfg->offset + offset, val, - bytes / priv->cfg.stride); +if (offset + bytes > dcfg->size) +return -EINVAL ; + +if (bytes == (REGMAP_FIELD_SIZE >>3 )) { + field_id = offset /REGMAP_FIELDS_PER_REG ; + ret =regmap_field_read (priv->reg_fields[field_id], ®val); +if (ret) +return ret; + + *(u16 *)(val) = regval; + }else { + ret =regmap_bulk_read (priv->regmap, dcfg->offset + offset, val, + bytes / priv->cfg.stride); +if (ret) +return ret; + } +return 0 ; }static int snvs_lpgpr_probe (struct platform_device *pdev)
@@ -94,6 +129,8 @@ static int snvs_lpgpr_probe(struct platform_device *pdev)struct nvmem_config *cfg;struct nvmem_device *nvmem;const struct snvs_lpgpr_cfg *dcfg; +int i; +int fields_count;if (!node)return -ENOENT ;
@@ -121,13 +158,31 @@ static int snvs_lpgpr_probe(struct platform_device *pdev) cfg->priv = priv; cfg->name =dev_name (dev); cfg->dev = dev; - cfg->stride =4 ; - cfg->word_size =4 ; + cfg->stride =2 ; + cfg->word_size =2 ; cfg->size = dcfg->size; cfg->owner =THIS_MODULE ; cfg->reg_read = snvs_lpgpr_read; cfg->reg_write = snvs_lpgpr_write; + fields_count = priv->dcfg->size / priv->cfg.stride; + priv->reg_fields =devm_kzalloc ( + dev,sizeof (struct regmap_field *) * fields_count,GFP_KERNEL ); +if (!priv->reg_fields) +return -ENOMEM ; + +for (i =0 ; i < fields_count; i++) { +size_t field_start = i *REGMAP_FIELD_SIZE ; +size_t field_end = field_start +REGMAP_FIELD_SIZE -1 ; +const struct reg_field field = +REG_FIELD (dcfg->offset, field_start, field_end); + + priv->reg_fields[i] = +devm_regmap_field_alloc (dev, priv->regmap, field); +if (IS_ERR (priv->reg_fields[i])) +return PTR_ERR (priv->reg_fields[i]); + } + nvmem =devm_nvmem_register (dev, cfg);return PTR_ERR_OR_ZERO (nvmem);
Signature · 1 line
2.26.3