patman: Convert camel case in gitutil.py
[platform/kernel/u-boot.git] / drivers / bootcount / bootcount_syscon.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) Vaisala Oyj. All rights reserved.
4  */
5
6 #include <common.h>
7 #include <bootcount.h>
8 #include <dm.h>
9 #include <dm/device_compat.h>
10 #include <linux/ioport.h>
11 #include <regmap.h>
12 #include <syscon.h>
13
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)))))
21
22 /**
23  * struct bootcount_syscon_priv - driver's private data
24  *
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
33  */
34 struct bootcount_syscon_priv {
35         struct regmap *regmap;
36         fdt_addr_t reg_addr;
37         fdt_size_t size;
38         u32 magic;
39         u32 magic_mask;
40         u32 reg_mask;
41         int shift;
42 };
43
44 static int bootcount_syscon_set(struct udevice *dev, const u32 val)
45 {
46         struct bootcount_syscon_priv *priv = dev_get_priv(dev);
47         u32 regval;
48
49         if ((val & priv->magic_mask) != 0)
50                 return -EINVAL;
51
52         regval = (priv->magic & priv->magic_mask) | (val & ~priv->magic_mask);
53
54         if (priv->size == 2) {
55                 regval &= 0xffff;
56                 regval |= (regval & 0xffff) << BYTES_TO_BITS(priv->size);
57         }
58
59         debug("%s: Prepare to write reg value: 0x%08x with register mask: 0x%08x\n",
60               __func__, regval, priv->reg_mask);
61
62         return regmap_update_bits(priv->regmap, priv->reg_addr, priv->reg_mask,
63                                   regval);
64 }
65
66 static int bootcount_syscon_get(struct udevice *dev, u32 *val)
67 {
68         struct bootcount_syscon_priv *priv = dev_get_priv(dev);
69         u32 regval;
70         int ret;
71
72         ret = regmap_read(priv->regmap, priv->reg_addr, &regval);
73         if (ret)
74                 return ret;
75
76         regval &= priv->reg_mask;
77         regval >>= priv->shift;
78
79         if ((regval & priv->magic_mask) == (priv->magic & priv->magic_mask)) {
80                 *val = regval & ~priv->magic_mask;
81         } else {
82                 dev_err(dev, "%s: Invalid bootcount magic\n", __func__);
83                 return -EINVAL;
84         }
85
86         debug("%s: Read bootcount value: 0x%08x from regval: 0x%08x\n",
87               __func__, *val, regval);
88         return 0;
89 }
90
91 static int bootcount_syscon_of_to_plat(struct udevice *dev)
92 {
93         struct bootcount_syscon_priv *priv = dev_get_priv(dev);
94         fdt_addr_t bootcount_offset;
95         fdt_size_t reg_size;
96
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);
102         }
103
104         priv->reg_addr = dev_read_addr_size_name(dev, "syscon_reg", &reg_size);
105         if (priv->reg_addr == FDT_ADDR_T_NONE) {
106                 dev_err(dev, "%s: syscon_reg address not found\n", __func__);
107                 return -EINVAL;
108         }
109         if (reg_size != 4) {
110                 dev_err(dev, "%s: Unsupported register size: %pa\n", __func__,
111                         &reg_size);
112                 return -EINVAL;
113         }
114
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__);
118                 return -EINVAL;
119         }
120         if (bootcount_offset + priv->size > reg_size) {
121                 dev_err(dev,
122                         "%s: Bootcount value doesn't fit in the reserved space\n",
123                         __func__);
124                 return -EINVAL;
125         }
126         if (priv->size != 2 && priv->size != 4) {
127                 dev_err(dev,
128                         "%s: Driver supports only 2 and 4 bytes bootcount size\n",
129                         __func__);
130                 return -EINVAL;
131         }
132
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);
138
139         return 0;
140 }
141
142 static const struct bootcount_ops bootcount_syscon_ops = {
143         .get = bootcount_syscon_get,
144         .set = bootcount_syscon_set,
145 };
146
147 static const struct udevice_id bootcount_syscon_ids[] = {
148         { .compatible = "u-boot,bootcount-syscon" },
149         {}
150 };
151
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,
159 };