kernel: bump 5.10 to 5.10.168
[openwrt/openwrt.git] / target / linux / generic / backport-5.10 / 801-v5.11-0001-nvmem-core-Add-support-for-keepout-regions.patch
1 From fd3bb8f54a88107570334c156efb0c724a261003 Mon Sep 17 00:00:00 2001
2 From: Evan Green <evgreen@chromium.org>
3 Date: Fri, 27 Nov 2020 10:28:34 +0000
4 Subject: [PATCH] nvmem: core: Add support for keepout regions
5
6 Introduce support into the nvmem core for arrays of register ranges
7 that should not result in actual device access. For these regions a
8 constant byte (repeated) is returned instead on read, and writes are
9 quietly ignored and returned as successful.
10
11 This is useful for instance if certain efuse regions are protected
12 from access by Linux because they contain secret info to another part
13 of the system (like an integrated modem).
14
15 Signed-off-by: Evan Green <evgreen@chromium.org>
16 Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
17 Link: https://lore.kernel.org/r/20201127102837.19366-3-srinivas.kandagatla@linaro.org
18 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
19 ---
20 drivers/nvmem/core.c | 153 ++++++++++++++++++++++++++++++++-
21 include/linux/nvmem-provider.h | 17 ++++
22 2 files changed, 166 insertions(+), 4 deletions(-)
23
24 --- a/drivers/nvmem/core.c
25 +++ b/drivers/nvmem/core.c
26 @@ -34,6 +34,8 @@ struct nvmem_device {
27 struct bin_attribute eeprom;
28 struct device *base_dev;
29 struct list_head cells;
30 + const struct nvmem_keepout *keepout;
31 + unsigned int nkeepout;
32 nvmem_reg_read_t reg_read;
33 nvmem_reg_write_t reg_write;
34 struct gpio_desc *wp_gpio;
35 @@ -66,8 +68,8 @@ static LIST_HEAD(nvmem_lookup_list);
36
37 static BLOCKING_NOTIFIER_HEAD(nvmem_notifier);
38
39 -static int nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
40 - void *val, size_t bytes)
41 +static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
42 + void *val, size_t bytes)
43 {
44 if (nvmem->reg_read)
45 return nvmem->reg_read(nvmem->priv, offset, val, bytes);
46 @@ -75,8 +77,8 @@ static int nvmem_reg_read(struct nvmem_d
47 return -EINVAL;
48 }
49
50 -static int nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
51 - void *val, size_t bytes)
52 +static int __nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
53 + void *val, size_t bytes)
54 {
55 int ret;
56
57 @@ -90,6 +92,88 @@ static int nvmem_reg_write(struct nvmem_
58 return -EINVAL;
59 }
60
61 +static int nvmem_access_with_keepouts(struct nvmem_device *nvmem,
62 + unsigned int offset, void *val,
63 + size_t bytes, int write)
64 +{
65 +
66 + unsigned int end = offset + bytes;
67 + unsigned int kend, ksize;
68 + const struct nvmem_keepout *keepout = nvmem->keepout;
69 + const struct nvmem_keepout *keepoutend = keepout + nvmem->nkeepout;
70 + int rc;
71 +
72 + /*
73 + * Skip all keepouts before the range being accessed.
74 + * Keepouts are sorted.
75 + */
76 + while ((keepout < keepoutend) && (keepout->end <= offset))
77 + keepout++;
78 +
79 + while ((offset < end) && (keepout < keepoutend)) {
80 + /* Access the valid portion before the keepout. */
81 + if (offset < keepout->start) {
82 + kend = min(end, keepout->start);
83 + ksize = kend - offset;
84 + if (write)
85 + rc = __nvmem_reg_write(nvmem, offset, val, ksize);
86 + else
87 + rc = __nvmem_reg_read(nvmem, offset, val, ksize);
88 +
89 + if (rc)
90 + return rc;
91 +
92 + offset += ksize;
93 + val += ksize;
94 + }
95 +
96 + /*
97 + * Now we're aligned to the start of this keepout zone. Go
98 + * through it.
99 + */
100 + kend = min(end, keepout->end);
101 + ksize = kend - offset;
102 + if (!write)
103 + memset(val, keepout->value, ksize);
104 +
105 + val += ksize;
106 + offset += ksize;
107 + keepout++;
108 + }
109 +
110 + /*
111 + * If we ran out of keepouts but there's still stuff to do, send it
112 + * down directly
113 + */
114 + if (offset < end) {
115 + ksize = end - offset;
116 + if (write)
117 + return __nvmem_reg_write(nvmem, offset, val, ksize);
118 + else
119 + return __nvmem_reg_read(nvmem, offset, val, ksize);
120 + }
121 +
122 + return 0;
123 +}
124 +
125 +static int nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
126 + void *val, size_t bytes)
127 +{
128 + if (!nvmem->nkeepout)
129 + return __nvmem_reg_read(nvmem, offset, val, bytes);
130 +
131 + return nvmem_access_with_keepouts(nvmem, offset, val, bytes, false);
132 +}
133 +
134 +static int nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
135 + void *val, size_t bytes)
136 +{
137 + if (!nvmem->nkeepout)
138 + return __nvmem_reg_write(nvmem, offset, val, bytes);
139 +
140 + return nvmem_access_with_keepouts(nvmem, offset, val, bytes, true);
141 +}
142 +
143 #ifdef CONFIG_NVMEM_SYSFS
144 static const char * const nvmem_type_str[] = {
145 [NVMEM_TYPE_UNKNOWN] = "Unknown",
146 @@ -535,6 +619,59 @@ nvmem_find_cell_by_name(struct nvmem_dev
147 return cell;
148 }
149
150 +static int nvmem_validate_keepouts(struct nvmem_device *nvmem)
151 +{
152 + unsigned int cur = 0;
153 + const struct nvmem_keepout *keepout = nvmem->keepout;
154 + const struct nvmem_keepout *keepoutend = keepout + nvmem->nkeepout;
155 +
156 + while (keepout < keepoutend) {
157 + /* Ensure keepouts are sorted and don't overlap. */
158 + if (keepout->start < cur) {
159 + dev_err(&nvmem->dev,
160 + "Keepout regions aren't sorted or overlap.\n");
161 +
162 + return -ERANGE;
163 + }
164 +
165 + if (keepout->end < keepout->start) {
166 + dev_err(&nvmem->dev,
167 + "Invalid keepout region.\n");
168 +
169 + return -EINVAL;
170 + }
171 +
172 + /*
173 + * Validate keepouts (and holes between) don't violate
174 + * word_size constraints.
175 + */
176 + if ((keepout->end - keepout->start < nvmem->word_size) ||
177 + ((keepout->start != cur) &&
178 + (keepout->start - cur < nvmem->word_size))) {
179 +
180 + dev_err(&nvmem->dev,
181 + "Keepout regions violate word_size constraints.\n");
182 +
183 + return -ERANGE;
184 + }
185 +
186 + /* Validate keepouts don't violate stride (alignment). */
187 + if (!IS_ALIGNED(keepout->start, nvmem->stride) ||
188 + !IS_ALIGNED(keepout->end, nvmem->stride)) {
189 +
190 + dev_err(&nvmem->dev,
191 + "Keepout regions violate stride.\n");
192 +
193 + return -EINVAL;
194 + }
195 +
196 + cur = keepout->end;
197 + keepout++;
198 + }
199 +
200 + return 0;
201 +}
202 +
203 static int nvmem_add_cells_from_of(struct nvmem_device *nvmem)
204 {
205 struct device_node *parent, *child;
206 @@ -656,6 +793,8 @@ struct nvmem_device *nvmem_register(cons
207 nvmem->type = config->type;
208 nvmem->reg_read = config->reg_read;
209 nvmem->reg_write = config->reg_write;
210 + nvmem->keepout = config->keepout;
211 + nvmem->nkeepout = config->nkeepout;
212 if (!config->no_of_node)
213 nvmem->dev.of_node = config->dev->of_node;
214
215 @@ -680,6 +819,12 @@ struct nvmem_device *nvmem_register(cons
216 nvmem->dev.groups = nvmem_dev_groups;
217 #endif
218
219 + if (nvmem->nkeepout) {
220 + rval = nvmem_validate_keepouts(nvmem);
221 + if (rval)
222 + goto err_put_device;
223 + }
224 +
225 dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name);
226
227 rval = device_register(&nvmem->dev);
228 --- a/include/linux/nvmem-provider.h
229 +++ b/include/linux/nvmem-provider.h
230 @@ -31,6 +31,19 @@ enum nvmem_type {
231 #define NVMEM_DEVID_AUTO (-2)
232
233 /**
234 + * struct nvmem_keepout - NVMEM register keepout range.
235 + *
236 + * @start: The first byte offset to avoid.
237 + * @end: One beyond the last byte offset to avoid.
238 + * @value: The byte to fill reads with for this region.
239 + */
240 +struct nvmem_keepout {
241 + unsigned int start;
242 + unsigned int end;
243 + unsigned char value;
244 +};
245 +
246 +/**
247 * struct nvmem_config - NVMEM device configuration
248 *
249 * @dev: Parent device.
250 @@ -39,6 +52,8 @@ enum nvmem_type {
251 * @owner: Pointer to exporter module. Used for refcounting.
252 * @cells: Optional array of pre-defined NVMEM cells.
253 * @ncells: Number of elements in cells.
254 + * @keepout: Optional array of keepout ranges (sorted ascending by start).
255 + * @nkeepout: Number of elements in the keepout array.
256 * @type: Type of the nvmem storage
257 * @read_only: Device is read-only.
258 * @root_only: Device is accessibly to root only.
259 @@ -67,6 +82,8 @@ struct nvmem_config {
260 struct gpio_desc *wp_gpio;
261 const struct nvmem_cell_info *cells;
262 int ncells;
263 + const struct nvmem_keepout *keepout;
264 + unsigned int nkeepout;
265 enum nvmem_type type;
266 bool read_only;
267 bool root_only;