1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) Vaisala Oyj. All rights reserved.
9 #include <dm/device_compat.h>
10 #include <linux/ioport.h>
14 #define BYTES_TO_BITS(bytes) ((bytes) << 3)
15 #define GEN_REG_MASK(val_size, val_addr) \
16 (GENMASK(BYTES_TO_BITS(val_size) - 1, 0) \
17 << (!!((val_addr) == 0x02) * BYTES_TO_BITS(2)))
18 #define GET_DEFAULT_VALUE(val_size) \
19 (CONFIG_SYS_BOOTCOUNT_MAGIC >> \
20 (BYTES_TO_BITS((sizeof(u32) - (val_size)))))
23 * struct bootcount_syscon_priv - driver's private data
25 * @regmap: syscon regmap
26 * @reg_addr: register address used to store the bootcount value
27 * @size: size of the bootcount value (2 or 4 bytes)
28 * @magic: magic used to validate/save the bootcount value
29 * @magic_mask: magic value bitmask
30 * @reg_mask: mask used to identify the location of the bootcount value
31 * in the register when 2 bytes length is used
32 * @shift: value used to extract the botcount value from the register
34 struct bootcount_syscon_priv {
35 struct regmap *regmap;
44 static int bootcount_syscon_set(struct udevice *dev, const u32 val)
46 struct bootcount_syscon_priv *priv = dev_get_priv(dev);
49 if ((val & priv->magic_mask) != 0)
52 regval = (priv->magic & priv->magic_mask) | (val & ~priv->magic_mask);
54 if (priv->size == 2) {
56 regval |= (regval & 0xffff) << BYTES_TO_BITS(priv->size);
59 debug("%s: Prepare to write reg value: 0x%08x with register mask: 0x%08x\n",
60 __func__, regval, priv->reg_mask);
62 return regmap_update_bits(priv->regmap, priv->reg_addr, priv->reg_mask,
66 static int bootcount_syscon_get(struct udevice *dev, u32 *val)
68 struct bootcount_syscon_priv *priv = dev_get_priv(dev);
72 ret = regmap_read(priv->regmap, priv->reg_addr, ®val);
76 regval &= priv->reg_mask;
77 regval >>= priv->shift;
79 if ((regval & priv->magic_mask) == (priv->magic & priv->magic_mask)) {
80 *val = regval & ~priv->magic_mask;
82 dev_err(dev, "%s: Invalid bootcount magic\n", __func__);
86 debug("%s: Read bootcount value: 0x%08x from regval: 0x%08x\n",
87 __func__, *val, regval);
91 static int bootcount_syscon_of_to_plat(struct udevice *dev)
93 struct bootcount_syscon_priv *priv = dev_get_priv(dev);
94 fdt_addr_t bootcount_offset;
97 priv->regmap = syscon_regmap_lookup_by_phandle(dev, "syscon");
98 if (IS_ERR(priv->regmap)) {
99 dev_err(dev, "%s: Unable to find regmap (%ld)\n", __func__,
100 PTR_ERR(priv->regmap));
101 return PTR_ERR(priv->regmap);
104 priv->reg_addr = dev_read_addr_size_name(dev, "syscon_reg", ®_size);
105 if (priv->reg_addr == FDT_ADDR_T_NONE) {
106 dev_err(dev, "%s: syscon_reg address not found\n", __func__);
110 dev_err(dev, "%s: Unsupported register size: %pa\n", __func__,
115 bootcount_offset = dev_read_addr_size_name(dev, "offset", &priv->size);
116 if (bootcount_offset == FDT_ADDR_T_NONE) {
117 dev_err(dev, "%s: offset configuration not found\n", __func__);
120 if (bootcount_offset + priv->size > reg_size) {
122 "%s: Bootcount value doesn't fit in the reserved space\n",
126 if (priv->size != 2 && priv->size != 4) {
128 "%s: Driver supports only 2 and 4 bytes bootcount size\n",
133 priv->magic = GET_DEFAULT_VALUE(priv->size);
134 priv->magic_mask = GENMASK(BYTES_TO_BITS(priv->size) - 1,
135 BYTES_TO_BITS(priv->size >> 1));
136 priv->shift = !!(bootcount_offset == 0x02) * BYTES_TO_BITS(priv->size);
137 priv->reg_mask = GEN_REG_MASK(priv->size, bootcount_offset);
142 static const struct bootcount_ops bootcount_syscon_ops = {
143 .get = bootcount_syscon_get,
144 .set = bootcount_syscon_set,
147 static const struct udevice_id bootcount_syscon_ids[] = {
148 { .compatible = "u-boot,bootcount-syscon" },
152 U_BOOT_DRIVER(bootcount_syscon) = {
153 .name = "bootcount-syscon",
154 .id = UCLASS_BOOTCOUNT,
155 .of_to_plat = bootcount_syscon_of_to_plat,
156 .priv_auto = sizeof(struct bootcount_syscon_priv),
157 .of_match = bootcount_syscon_ids,
158 .ops = &bootcount_syscon_ops,