firmware-utils: update to git HEAD
[openwrt/staging/stintel.git] / target / linux / bcm53xx / patches-6.1 / 181-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch
1 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
2 Date: Thu, 14 Sep 2023 07:59:09 +0200
3 Subject: [PATCH] nvmem: brcm_nvram: store a copy of NVRAM content
4 MIME-Version: 1.0
5 Content-Type: text/plain; charset=UTF-8
6 Content-Transfer-Encoding: 8bit
7
8 This driver uses MMIO access for reading NVRAM from a flash device.
9 Underneath there is a flash controller that reads data and provides
10 mapping window.
11
12 Using MMIO interface affects controller configuration and may break real
13 controller driver. It was reported by multiple users of devices with
14 NVRAM stored on NAND.
15
16 Modify driver to read & cache NVRAM content during init and use that
17 copy to provide NVMEM data when requested. On NAND flashes due to their
18 alignment NVRAM partitions can be quite big (1 MiB and more) while
19 actual NVRAM content stays quite small (usually 16 to 32 KiB). To avoid
20 allocating so much memory check for actual data length.
21
22 Link: https://lore.kernel.org/linux-mtd/CACna6rwf3_9QVjYcM+847biTX=K0EoWXuXcSMkJO1Vy_5vmVqA@mail.gmail.com/
23 Fixes: 3fef9ed0627a ("nvmem: brcm_nvram: new driver exposing Broadcom's NVRAM")
24 Cc: Arınç ÜNAL <arinc.unal@arinc9.com>
25 Cc: Florian Fainelli <florian.fainelli@broadcom.com>
26 Cc: Scott Branden <scott.branden@broadcom.com>
27 Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
28 Acked-by: Arınç ÜNAL <arinc.unal@arinc9.com>
29 ---
30 drivers/nvmem/brcm_nvram.c | 134 ++++++++++++++++++++++++++-----------
31 1 file changed, 94 insertions(+), 40 deletions(-)
32
33 --- a/drivers/nvmem/brcm_nvram.c
34 +++ b/drivers/nvmem/brcm_nvram.c
35 @@ -17,9 +17,23 @@
36
37 #define NVRAM_MAGIC "FLSH"
38
39 +/**
40 + * struct brcm_nvram - driver state internal struct
41 + *
42 + * @dev: NVMEM device pointer
43 + * @nvmem_size: Size of the whole space available for NVRAM
44 + * @data: NVRAM data copy stored to avoid poking underlaying flash controller
45 + * @data_len: NVRAM data size
46 + * @padding_byte: Padding value used to fill remaining space
47 + * @cells: Array of discovered NVMEM cells
48 + * @ncells: Number of elements in cells
49 + */
50 struct brcm_nvram {
51 struct device *dev;
52 - void __iomem *base;
53 + size_t nvmem_size;
54 + uint8_t *data;
55 + size_t data_len;
56 + uint8_t padding_byte;
57 struct nvmem_cell_info *cells;
58 int ncells;
59 };
60 @@ -36,10 +50,47 @@ static int brcm_nvram_read(void *context
61 size_t bytes)
62 {
63 struct brcm_nvram *priv = context;
64 - u8 *dst = val;
65 + size_t to_copy;
66 +
67 + if (offset + bytes > priv->data_len)
68 + to_copy = max_t(ssize_t, (ssize_t)priv->data_len - offset, 0);
69 + else
70 + to_copy = bytes;
71 +
72 + memcpy(val, priv->data + offset, to_copy);
73 +
74 + memset((uint8_t *)val + to_copy, priv->padding_byte, bytes - to_copy);
75 +
76 + return 0;
77 +}
78 +
79 +static int brcm_nvram_copy_data(struct brcm_nvram *priv, struct platform_device *pdev)
80 +{
81 + struct resource *res;
82 + void __iomem *base;
83 +
84 + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
85 + if (IS_ERR(base))
86 + return PTR_ERR(base);
87 +
88 + priv->nvmem_size = resource_size(res);
89 +
90 + priv->padding_byte = readb(base + priv->nvmem_size - 1);
91 + for (priv->data_len = priv->nvmem_size;
92 + priv->data_len;
93 + priv->data_len--) {
94 + if (readb(base + priv->data_len - 1) != priv->padding_byte)
95 + break;
96 + }
97 + WARN(priv->data_len > SZ_128K, "Unexpected (big) NVRAM size: %zu B\n", priv->data_len);
98
99 - while (bytes--)
100 - *dst++ = readb(priv->base + offset++);
101 + priv->data = devm_kzalloc(priv->dev, priv->data_len, GFP_KERNEL);
102 + if (!priv->data)
103 + return -ENOMEM;
104 +
105 + memcpy_fromio(priv->data, base, priv->data_len);
106 +
107 + bcm47xx_nvram_init_from_iomem(base, priv->data_len);
108
109 return 0;
110 }
111 @@ -67,8 +118,13 @@ static int brcm_nvram_add_cells(struct b
112 size_t len)
113 {
114 struct device *dev = priv->dev;
115 - char *var, *value, *eq;
116 + char *var, *value;
117 + uint8_t tmp;
118 int idx;
119 + int err = 0;
120 +
121 + tmp = priv->data[len - 1];
122 + priv->data[len - 1] = '\0';
123
124 priv->ncells = 0;
125 for (var = data + sizeof(struct brcm_nvram_header);
126 @@ -78,67 +134,68 @@ static int brcm_nvram_add_cells(struct b
127 }
128
129 priv->cells = devm_kcalloc(dev, priv->ncells, sizeof(*priv->cells), GFP_KERNEL);
130 - if (!priv->cells)
131 - return -ENOMEM;
132 + if (!priv->cells) {
133 + err = -ENOMEM;
134 + goto out;
135 + }
136
137 for (var = data + sizeof(struct brcm_nvram_header), idx = 0;
138 var < (char *)data + len && *var;
139 var = value + strlen(value) + 1, idx++) {
140 + char *eq, *name;
141 +
142 eq = strchr(var, '=');
143 if (!eq)
144 break;
145 *eq = '\0';
146 + name = devm_kstrdup(dev, var, GFP_KERNEL);
147 + *eq = '=';
148 + if (!name) {
149 + err = -ENOMEM;
150 + goto out;
151 + }
152 value = eq + 1;
153
154 - priv->cells[idx].name = devm_kstrdup(dev, var, GFP_KERNEL);
155 - if (!priv->cells[idx].name)
156 - return -ENOMEM;
157 + priv->cells[idx].name = name;
158 priv->cells[idx].offset = value - (char *)data;
159 priv->cells[idx].bytes = strlen(value);
160 priv->cells[idx].np = of_get_child_by_name(dev->of_node, priv->cells[idx].name);
161 - if (!strcmp(var, "et0macaddr") ||
162 - !strcmp(var, "et1macaddr") ||
163 - !strcmp(var, "et2macaddr")) {
164 + if (!strcmp(name, "et0macaddr") ||
165 + !strcmp(name, "et1macaddr") ||
166 + !strcmp(name, "et2macaddr")) {
167 priv->cells[idx].raw_len = strlen(value);
168 priv->cells[idx].bytes = ETH_ALEN;
169 priv->cells[idx].read_post_process = brcm_nvram_read_post_process_macaddr;
170 }
171 }
172
173 - return 0;
174 +out:
175 + priv->data[len - 1] = tmp;
176 + return err;
177 }
178
179 static int brcm_nvram_parse(struct brcm_nvram *priv)
180 {
181 + struct brcm_nvram_header *header = (struct brcm_nvram_header *)priv->data;
182 struct device *dev = priv->dev;
183 - struct brcm_nvram_header header;
184 - uint8_t *data;
185 size_t len;
186 int err;
187
188 - memcpy_fromio(&header, priv->base, sizeof(header));
189 -
190 - if (memcmp(header.magic, NVRAM_MAGIC, 4)) {
191 + if (memcmp(header->magic, NVRAM_MAGIC, 4)) {
192 dev_err(dev, "Invalid NVRAM magic\n");
193 return -EINVAL;
194 }
195
196 - len = le32_to_cpu(header.len);
197 -
198 - data = kzalloc(len, GFP_KERNEL);
199 - if (!data)
200 - return -ENOMEM;
201 -
202 - memcpy_fromio(data, priv->base, len);
203 - data[len - 1] = '\0';
204 -
205 - err = brcm_nvram_add_cells(priv, data, len);
206 - if (err) {
207 - dev_err(dev, "Failed to add cells: %d\n", err);
208 - return err;
209 + len = le32_to_cpu(header->len);
210 + if (len > priv->nvmem_size) {
211 + dev_err(dev, "NVRAM length (%zd) exceeds mapped size (%zd)\n", len,
212 + priv->nvmem_size);
213 + return -EINVAL;
214 }
215
216 - kfree(data);
217 + err = brcm_nvram_add_cells(priv, priv->data, len);
218 + if (err)
219 + dev_err(dev, "Failed to add cells: %d\n", err);
220
221 return 0;
222 }
223 @@ -150,7 +207,6 @@ static int brcm_nvram_probe(struct platf
224 .reg_read = brcm_nvram_read,
225 };
226 struct device *dev = &pdev->dev;
227 - struct resource *res;
228 struct brcm_nvram *priv;
229 int err;
230
231 @@ -159,21 +215,19 @@ static int brcm_nvram_probe(struct platf
232 return -ENOMEM;
233 priv->dev = dev;
234
235 - priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
236 - if (IS_ERR(priv->base))
237 - return PTR_ERR(priv->base);
238 + err = brcm_nvram_copy_data(priv, pdev);
239 + if (err)
240 + return err;
241
242 err = brcm_nvram_parse(priv);
243 if (err)
244 return err;
245
246 - bcm47xx_nvram_init_from_iomem(priv->base, resource_size(res));
247 -
248 config.dev = dev;
249 config.cells = priv->cells;
250 config.ncells = priv->ncells;
251 config.priv = priv;
252 - config.size = resource_size(res);
253 + config.size = priv->nvmem_size;
254
255 return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config));
256 }