]> Git Repo - J-u-boot.git/blob - drivers/bootcount/bootcount_syscon.c
net: fec: phy: Don't advertise Gbit on (R)MII
[J-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 <bootcount.h>
7 #include <dm.h>
8 #include <dm/device_compat.h>
9 #include <linux/ioport.h>
10 #include <regmap.h>
11 #include <syscon.h>
12
13 #define BYTES_TO_BITS(bytes) ((bytes) << 3)
14 #define GEN_REG_MASK(val_size, val_addr)                                       \
15         (GENMASK(BYTES_TO_BITS(val_size) - 1, 0)                               \
16          << (!!((val_addr) == 0x02) * BYTES_TO_BITS(2)))
17 #define GET_DEFAULT_VALUE(val_size)                                            \
18         (CONFIG_SYS_BOOTCOUNT_MAGIC >>                                         \
19          (BYTES_TO_BITS((sizeof(u32) - (val_size)))))
20
21 /**
22  * struct bootcount_syscon_priv - driver's private data
23  *
24  * @regmap: syscon regmap
25  * @reg_addr: register address used to store the bootcount value
26  * @size: size of the bootcount value (2 or 4 bytes)
27  * @magic: magic used to validate/save the bootcount value
28  * @magic_mask: magic value bitmask
29  * @reg_mask: mask used to identify the location of the bootcount value
30  * in the register when 2 bytes length is used
31  * @shift: value used to extract the botcount value from the register
32  */
33 struct bootcount_syscon_priv {
34         struct regmap *regmap;
35         fdt_addr_t reg_addr;
36         fdt_size_t size;
37         u32 magic;
38         u32 magic_mask;
39         u32 reg_mask;
40         int shift;
41 };
42
43 static int bootcount_syscon_set(struct udevice *dev, const u32 val)
44 {
45         struct bootcount_syscon_priv *priv = dev_get_priv(dev);
46         u32 regval;
47
48         if ((val & priv->magic_mask) != 0)
49                 return -EINVAL;
50
51         regval = (priv->magic & priv->magic_mask) | (val & ~priv->magic_mask);
52
53         if (priv->size == 2) {
54                 regval &= 0xffff;
55                 regval |= (regval & 0xffff) << BYTES_TO_BITS(priv->size);
56         }
57
58         debug("%s: Prepare to write reg value: 0x%08x with register mask: 0x%08x\n",
59               __func__, regval, priv->reg_mask);
60
61         return regmap_update_bits(priv->regmap, priv->reg_addr, priv->reg_mask,
62                                   regval);
63 }
64
65 static int bootcount_syscon_get(struct udevice *dev, u32 *val)
66 {
67         struct bootcount_syscon_priv *priv = dev_get_priv(dev);
68         u32 regval;
69         int ret;
70
71         ret = regmap_read(priv->regmap, priv->reg_addr, &regval);
72         if (ret)
73                 return ret;
74
75         regval &= priv->reg_mask;
76         regval >>= priv->shift;
77
78         if ((regval & priv->magic_mask) == (priv->magic & priv->magic_mask)) {
79                 *val = regval & ~priv->magic_mask;
80         } else {
81                 dev_err(dev, "%s: Invalid bootcount magic\n", __func__);
82                 return -EINVAL;
83         }
84
85         debug("%s: Read bootcount value: 0x%08x from regval: 0x%08x\n",
86               __func__, *val, regval);
87         return 0;
88 }
89
90 static int bootcount_syscon_of_to_plat(struct udevice *dev)
91 {
92         struct bootcount_syscon_priv *priv = dev_get_priv(dev);
93         fdt_addr_t bootcount_offset;
94         fdt_size_t reg_size;
95
96         priv->regmap = syscon_regmap_lookup_by_phandle(dev, "syscon");
97         if (IS_ERR(priv->regmap)) {
98                 dev_err(dev, "%s: Unable to find regmap (%ld)\n", __func__,
99                         PTR_ERR(priv->regmap));
100                 return PTR_ERR(priv->regmap);
101         }
102
103         priv->reg_addr = dev_read_addr_size_name(dev, "syscon_reg", &reg_size);
104         if (priv->reg_addr == FDT_ADDR_T_NONE) {
105                 dev_err(dev, "%s: syscon_reg address not found\n", __func__);
106                 return -EINVAL;
107         }
108         if (reg_size != 4) {
109                 dev_err(dev, "%s: Unsupported register size: %pa\n", __func__,
110                         &reg_size);
111                 return -EINVAL;
112         }
113
114         bootcount_offset = dev_read_addr_size_name(dev, "offset", &priv->size);
115         if (bootcount_offset == FDT_ADDR_T_NONE) {
116                 dev_err(dev, "%s: offset configuration not found\n", __func__);
117                 return -EINVAL;
118         }
119         if (bootcount_offset + priv->size > reg_size) {
120                 dev_err(dev,
121                         "%s: Bootcount value doesn't fit in the reserved space\n",
122                         __func__);
123                 return -EINVAL;
124         }
125         if (priv->size != 2 && priv->size != 4) {
126                 dev_err(dev,
127                         "%s: Driver supports only 2 and 4 bytes bootcount size\n",
128                         __func__);
129                 return -EINVAL;
130         }
131
132         priv->magic = GET_DEFAULT_VALUE(priv->size);
133         priv->magic_mask = GENMASK(BYTES_TO_BITS(priv->size) - 1,
134                                    BYTES_TO_BITS(priv->size >> 1));
135         priv->shift = !!(bootcount_offset == 0x02) * BYTES_TO_BITS(priv->size);
136         priv->reg_mask = GEN_REG_MASK(priv->size, bootcount_offset);
137
138         return 0;
139 }
140
141 static const struct bootcount_ops bootcount_syscon_ops = {
142         .get = bootcount_syscon_get,
143         .set = bootcount_syscon_set,
144 };
145
146 static const struct udevice_id bootcount_syscon_ids[] = {
147         { .compatible = "u-boot,bootcount-syscon" },
148         {}
149 };
150
151 U_BOOT_DRIVER(bootcount_syscon) = {
152         .name = "bootcount-syscon",
153         .id = UCLASS_BOOTCOUNT,
154         .of_to_plat = bootcount_syscon_of_to_plat,
155         .priv_auto = sizeof(struct bootcount_syscon_priv),
156         .of_match = bootcount_syscon_ids,
157         .ops = &bootcount_syscon_ops,
158 };
This page took 0.035067 seconds and 4 git commands to generate.