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 
bootcount_syscon_set(struct udevice * dev,const u32 val)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 
bootcount_syscon_get(struct udevice * dev,u32 * val)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 
bootcount_syscon_of_to_plat(struct udevice * dev)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 };
159