Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/dpll/zl3073x/fw.c
29268 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
3
#include <linux/array_size.h>
4
#include <linux/build_bug.h>
5
#include <linux/dev_printk.h>
6
#include <linux/err.h>
7
#include <linux/errno.h>
8
#include <linux/netlink.h>
9
#include <linux/slab.h>
10
#include <linux/string.h>
11
12
#include "core.h"
13
#include "flash.h"
14
#include "fw.h"
15
16
#define ZL3073X_FW_ERR_PFX "FW load failed: "
17
#define ZL3073X_FW_ERR_MSG(_extack, _msg, ...) \
18
NL_SET_ERR_MSG_FMT_MOD((_extack), ZL3073X_FW_ERR_PFX _msg, \
19
## __VA_ARGS__)
20
21
enum zl3073x_flash_type {
22
ZL3073X_FLASH_TYPE_NONE = 0,
23
ZL3073X_FLASH_TYPE_SECTORS,
24
ZL3073X_FLASH_TYPE_PAGE,
25
ZL3073X_FLASH_TYPE_PAGE_AND_COPY,
26
};
27
28
struct zl3073x_fw_component_info {
29
const char *name;
30
size_t max_size;
31
enum zl3073x_flash_type flash_type;
32
u32 load_addr;
33
u32 dest_page;
34
u32 copy_page;
35
};
36
37
static const struct zl3073x_fw_component_info component_info[] = {
38
[ZL_FW_COMPONENT_UTIL] = {
39
.name = "utility",
40
.max_size = 0x2300,
41
.load_addr = 0x20000000,
42
.flash_type = ZL3073X_FLASH_TYPE_NONE,
43
},
44
[ZL_FW_COMPONENT_FW1] = {
45
.name = "firmware1",
46
.max_size = 0x35000,
47
.load_addr = 0x20002000,
48
.flash_type = ZL3073X_FLASH_TYPE_SECTORS,
49
.dest_page = 0x020,
50
},
51
[ZL_FW_COMPONENT_FW2] = {
52
.name = "firmware2",
53
.max_size = 0x0040,
54
.load_addr = 0x20000000,
55
.flash_type = ZL3073X_FLASH_TYPE_PAGE_AND_COPY,
56
.dest_page = 0x3e0,
57
.copy_page = 0x000,
58
},
59
[ZL_FW_COMPONENT_FW3] = {
60
.name = "firmware3",
61
.max_size = 0x0248,
62
.load_addr = 0x20000400,
63
.flash_type = ZL3073X_FLASH_TYPE_PAGE_AND_COPY,
64
.dest_page = 0x3e4,
65
.copy_page = 0x004,
66
},
67
[ZL_FW_COMPONENT_CFG0] = {
68
.name = "config0",
69
.max_size = 0x1000,
70
.load_addr = 0x20000000,
71
.flash_type = ZL3073X_FLASH_TYPE_PAGE,
72
.dest_page = 0x3d0,
73
},
74
[ZL_FW_COMPONENT_CFG1] = {
75
.name = "config1",
76
.max_size = 0x1000,
77
.load_addr = 0x20000000,
78
.flash_type = ZL3073X_FLASH_TYPE_PAGE,
79
.dest_page = 0x3c0,
80
},
81
[ZL_FW_COMPONENT_CFG2] = {
82
.name = "config2",
83
.max_size = 0x1000,
84
.load_addr = 0x20000000,
85
.flash_type = ZL3073X_FLASH_TYPE_PAGE,
86
.dest_page = 0x3b0,
87
},
88
[ZL_FW_COMPONENT_CFG3] = {
89
.name = "config3",
90
.max_size = 0x1000,
91
.load_addr = 0x20000000,
92
.flash_type = ZL3073X_FLASH_TYPE_PAGE,
93
.dest_page = 0x3a0,
94
},
95
[ZL_FW_COMPONENT_CFG4] = {
96
.name = "config4",
97
.max_size = 0x1000,
98
.load_addr = 0x20000000,
99
.flash_type = ZL3073X_FLASH_TYPE_PAGE,
100
.dest_page = 0x390,
101
},
102
[ZL_FW_COMPONENT_CFG5] = {
103
.name = "config5",
104
.max_size = 0x1000,
105
.load_addr = 0x20000000,
106
.flash_type = ZL3073X_FLASH_TYPE_PAGE,
107
.dest_page = 0x380,
108
},
109
[ZL_FW_COMPONENT_CFG6] = {
110
.name = "config6",
111
.max_size = 0x1000,
112
.load_addr = 0x20000000,
113
.flash_type = ZL3073X_FLASH_TYPE_PAGE,
114
.dest_page = 0x370,
115
},
116
};
117
118
/* Sanity check */
119
static_assert(ARRAY_SIZE(component_info) == ZL_FW_NUM_COMPONENTS);
120
121
/**
122
* zl3073x_fw_component_alloc - Alloc structure to hold firmware component
123
* @size: size of buffer to store data
124
*
125
* Return: pointer to allocated component structure or NULL on error.
126
*/
127
static struct zl3073x_fw_component *
128
zl3073x_fw_component_alloc(size_t size)
129
{
130
struct zl3073x_fw_component *comp;
131
132
comp = kzalloc(sizeof(*comp), GFP_KERNEL);
133
if (!comp)
134
return NULL;
135
136
comp->size = size;
137
comp->data = kzalloc(size, GFP_KERNEL);
138
if (!comp->data) {
139
kfree(comp);
140
return NULL;
141
}
142
143
return comp;
144
}
145
146
/**
147
* zl3073x_fw_component_free - Free allocated component structure
148
* @comp: pointer to allocated component
149
*/
150
static void
151
zl3073x_fw_component_free(struct zl3073x_fw_component *comp)
152
{
153
if (comp)
154
kfree(comp->data);
155
156
kfree(comp);
157
}
158
159
/**
160
* zl3073x_fw_component_id_get - Get ID for firmware component name
161
* @name: input firmware component name
162
*
163
* Return:
164
* - ZL3073X_FW_COMPONENT_* ID for known component name
165
* - ZL3073X_FW_COMPONENT_INVALID if the given name is unknown
166
*/
167
static enum zl3073x_fw_component_id
168
zl3073x_fw_component_id_get(const char *name)
169
{
170
enum zl3073x_fw_component_id id;
171
172
for (id = 0; id < ZL_FW_NUM_COMPONENTS; id++)
173
if (!strcasecmp(name, component_info[id].name))
174
return id;
175
176
return ZL_FW_COMPONENT_INVALID;
177
}
178
179
/**
180
* zl3073x_fw_component_load - Load component from firmware source
181
* @zldev: zl3073x device structure
182
* @pcomp: pointer to loaded component
183
* @psrc: data pointer to load component from
184
* @psize: remaining bytes in buffer
185
* @extack: netlink extack pointer to report errors
186
*
187
* The function allocates single firmware component and loads the data from
188
* the buffer specified by @psrc and @psize. Pointer to allocated component
189
* is stored in output @pcomp. Source data pointer @psrc and remaining bytes
190
* @psize are updated accordingly.
191
*
192
* Return:
193
* * 1 when component was allocated and loaded
194
* * 0 when there is no component to load
195
* * <0 on error
196
*/
197
static ssize_t
198
zl3073x_fw_component_load(struct zl3073x_dev *zldev,
199
struct zl3073x_fw_component **pcomp,
200
const char **psrc, size_t *psize,
201
struct netlink_ext_ack *extack)
202
{
203
const struct zl3073x_fw_component_info *info;
204
struct zl3073x_fw_component *comp = NULL;
205
struct device *dev = zldev->dev;
206
enum zl3073x_fw_component_id id;
207
char buf[32], name[16];
208
u32 count, size, *dest;
209
int pos, rc;
210
211
/* Fetch image name and size from input */
212
strscpy(buf, *psrc, min(sizeof(buf), *psize));
213
rc = sscanf(buf, "%15s %u %n", name, &count, &pos);
214
if (!rc) {
215
/* No more data */
216
return 0;
217
} else if (rc == 1 || count > U32_MAX / sizeof(u32)) {
218
ZL3073X_FW_ERR_MSG(extack, "invalid component size");
219
return -EINVAL;
220
}
221
*psrc += pos;
222
*psize -= pos;
223
224
dev_dbg(dev, "Firmware component '%s' found\n", name);
225
226
id = zl3073x_fw_component_id_get(name);
227
if (id == ZL_FW_COMPONENT_INVALID) {
228
ZL3073X_FW_ERR_MSG(extack, "unknown component type '%s'", name);
229
return -EINVAL;
230
}
231
232
info = &component_info[id];
233
size = count * sizeof(u32); /* get size in bytes */
234
235
/* Check image size validity */
236
if (size > component_info[id].max_size) {
237
ZL3073X_FW_ERR_MSG(extack,
238
"[%s] component is too big (%u bytes)\n",
239
info->name, size);
240
return -EINVAL;
241
}
242
243
dev_dbg(dev, "Indicated component image size: %u bytes\n", size);
244
245
/* Alloc component */
246
comp = zl3073x_fw_component_alloc(size);
247
if (!comp) {
248
ZL3073X_FW_ERR_MSG(extack, "failed to alloc memory");
249
return -ENOMEM;
250
}
251
comp->id = id;
252
253
/* Load component data from firmware source */
254
for (dest = comp->data; count; count--, dest++) {
255
strscpy(buf, *psrc, min(sizeof(buf), *psize));
256
rc = sscanf(buf, "%x %n", dest, &pos);
257
if (!rc)
258
goto err_data;
259
260
*psrc += pos;
261
*psize -= pos;
262
}
263
264
*pcomp = comp;
265
266
return 1;
267
268
err_data:
269
ZL3073X_FW_ERR_MSG(extack, "[%s] invalid or missing data", info->name);
270
271
zl3073x_fw_component_free(comp);
272
273
return -ENODATA;
274
}
275
276
/**
277
* zl3073x_fw_free - Free allocated firmware
278
* @fw: firmware pointer
279
*
280
* The function frees existing firmware allocated by @zl3073x_fw_load.
281
*/
282
void zl3073x_fw_free(struct zl3073x_fw *fw)
283
{
284
size_t i;
285
286
if (!fw)
287
return;
288
289
for (i = 0; i < ZL_FW_NUM_COMPONENTS; i++)
290
zl3073x_fw_component_free(fw->component[i]);
291
292
kfree(fw);
293
}
294
295
/**
296
* zl3073x_fw_load - Load all components from source
297
* @zldev: zl3073x device structure
298
* @data: source buffer pointer
299
* @size: size of source buffer
300
* @extack: netlink extack pointer to report errors
301
*
302
* The functions allocate firmware structure and loads all components from
303
* the given buffer specified by @data and @size.
304
*
305
* Return: pointer to firmware on success, error pointer on error
306
*/
307
struct zl3073x_fw *zl3073x_fw_load(struct zl3073x_dev *zldev, const char *data,
308
size_t size, struct netlink_ext_ack *extack)
309
{
310
struct zl3073x_fw_component *comp;
311
enum zl3073x_fw_component_id id;
312
struct zl3073x_fw *fw;
313
ssize_t rc;
314
315
/* Allocate firmware structure */
316
fw = kzalloc(sizeof(*fw), GFP_KERNEL);
317
if (!fw)
318
return ERR_PTR(-ENOMEM);
319
320
do {
321
/* Load single component */
322
rc = zl3073x_fw_component_load(zldev, &comp, &data, &size,
323
extack);
324
if (rc <= 0)
325
/* Everything was read or error occurred */
326
break;
327
328
id = comp->id;
329
330
/* Report error if the given component is present twice
331
* or more.
332
*/
333
if (fw->component[id]) {
334
ZL3073X_FW_ERR_MSG(extack,
335
"duplicate component '%s' detected",
336
component_info[id].name);
337
zl3073x_fw_component_free(comp);
338
rc = -EINVAL;
339
break;
340
}
341
342
fw->component[id] = comp;
343
} while (true);
344
345
if (rc) {
346
/* Free allocated firmware in case of error */
347
zl3073x_fw_free(fw);
348
return ERR_PTR(rc);
349
}
350
351
return fw;
352
}
353
354
/**
355
* zl3073x_flash_bundle_flash - Flash all components
356
* @zldev: zl3073x device structure
357
* @components: pointer to components array
358
* @extack: netlink extack pointer to report errors
359
*
360
* Returns 0 in case of success or negative number otherwise.
361
*/
362
static int
363
zl3073x_fw_component_flash(struct zl3073x_dev *zldev,
364
struct zl3073x_fw_component *comp,
365
struct netlink_ext_ack *extack)
366
{
367
const struct zl3073x_fw_component_info *info;
368
int rc;
369
370
info = &component_info[comp->id];
371
372
switch (info->flash_type) {
373
case ZL3073X_FLASH_TYPE_NONE:
374
/* Non-flashable component - used for utility */
375
return 0;
376
case ZL3073X_FLASH_TYPE_SECTORS:
377
rc = zl3073x_flash_sectors(zldev, info->name, info->dest_page,
378
info->load_addr, comp->data,
379
comp->size, extack);
380
break;
381
case ZL3073X_FLASH_TYPE_PAGE:
382
rc = zl3073x_flash_page(zldev, info->name, info->dest_page,
383
info->load_addr, comp->data, comp->size,
384
extack);
385
break;
386
case ZL3073X_FLASH_TYPE_PAGE_AND_COPY:
387
rc = zl3073x_flash_page(zldev, info->name, info->dest_page,
388
info->load_addr, comp->data, comp->size,
389
extack);
390
if (!rc)
391
rc = zl3073x_flash_page_copy(zldev, info->name,
392
info->dest_page,
393
info->copy_page, extack);
394
break;
395
}
396
if (rc)
397
ZL3073X_FW_ERR_MSG(extack, "Failed to flash component '%s'",
398
info->name);
399
400
return rc;
401
}
402
403
int zl3073x_fw_flash(struct zl3073x_dev *zldev, struct zl3073x_fw *zlfw,
404
struct netlink_ext_ack *extack)
405
{
406
int i, rc = 0;
407
408
for (i = 0; i < ZL_FW_NUM_COMPONENTS; i++) {
409
if (!zlfw->component[i])
410
continue; /* Component is not present */
411
412
rc = zl3073x_fw_component_flash(zldev, zlfw->component[i],
413
extack);
414
if (rc)
415
break;
416
}
417
418
return rc;
419
}
420
421