stm32mp: add the command stm32prog
[oweals/u-boot.git] / arch / arm / mach-stm32mp / cmd_stm32prog / stm32prog.c
1 // SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
2 /*
3  * Copyright (C) 2020, STMicroelectronics - All Rights Reserved
4  */
5
6 #include <common.h>
7 #include <console.h>
8 #include <dfu.h>
9 #include <malloc.h>
10 #include <dm/uclass.h>
11 #include <linux/list.h>
12 #include <linux/list_sort.h>
13 #include <linux/sizes.h>
14
15 #include "stm32prog.h"
16
17 #define OPT_SELECT      BIT(0)
18 #define OPT_EMPTY       BIT(1)
19
20 #define IS_SELECT(part) ((part)->option & OPT_SELECT)
21 #define IS_EMPTY(part)  ((part)->option & OPT_EMPTY)
22
23 #define ALT_BUF_LEN                     SZ_1K
24
25 DECLARE_GLOBAL_DATA_PTR;
26
27 char *stm32prog_get_error(struct stm32prog_data *data)
28 {
29         static const char error_msg[] = "Unspecified";
30
31         if (strlen(data->error) == 0)
32                 strcpy(data->error, error_msg);
33
34         return data->error;
35 }
36
37 static int parse_flash_layout(struct stm32prog_data *data,
38                               ulong addr,
39                               ulong size)
40 {
41         return -ENODEV;
42 }
43
44 static int __init part_cmp(void *priv, struct list_head *a, struct list_head *b)
45 {
46         struct stm32prog_part_t *parta, *partb;
47
48         parta = container_of(a, struct stm32prog_part_t, list);
49         partb = container_of(b, struct stm32prog_part_t, list);
50
51         return parta->addr > partb->addr ? 1 : -1;
52 }
53
54 static int init_device(struct stm32prog_data *data,
55                        struct stm32prog_dev_t *dev)
56 {
57         struct blk_desc *block_dev = NULL;
58         int part_id;
59         u64 first_addr = 0, last_addr = 0;
60         struct stm32prog_part_t *part, *next_part;
61
62         switch (dev->target) {
63         default:
64                 stm32prog_err("unknown device type = %d", dev->target);
65                 return -ENODEV;
66         }
67
68         /* order partition list in offset order */
69         list_sort(NULL, &dev->part_list, &part_cmp);
70         part_id = 1;
71         pr_debug("id : Opt Phase     Name target.n dev.n addr     size     part_off part_size\n");
72         list_for_each_entry(part, &dev->part_list, list) {
73                 if (part->part_type == RAW_IMAGE) {
74                         part->part_id = 0x0;
75                         part->addr = 0x0;
76                         if (block_dev)
77                                 part->size = block_dev->lba * block_dev->blksz;
78                         else
79                                 part->size = last_addr;
80                         pr_debug("-- : %1d %02x %14s %02d %02d.%02d %08llx %08llx\n",
81                                  part->option, part->id, part->name,
82                                  part->part_type, part->target,
83                                  part->dev_id, part->addr, part->size);
84                         continue;
85                 }
86
87                 part->part_id = part_id++;
88
89                 /* last partition : size to the end of the device */
90                 if (part->list.next != &dev->part_list) {
91                         next_part =
92                                 container_of(part->list.next,
93                                              struct stm32prog_part_t,
94                                              list);
95                         if (part->addr < next_part->addr) {
96                                 part->size = next_part->addr -
97                                              part->addr;
98                         } else {
99                                 stm32prog_err("%s (0x%x): same address : 0x%llx == %s (0x%x): 0x%llx",
100                                               part->name, part->id,
101                                               part->addr,
102                                               next_part->name,
103                                               next_part->id,
104                                               next_part->addr);
105                                 return -EINVAL;
106                         }
107                 } else {
108                         if (part->addr <= last_addr) {
109                                 part->size = last_addr - part->addr;
110                         } else {
111                                 stm32prog_err("%s (0x%x): invalid address 0x%llx (max=0x%llx)",
112                                               part->name, part->id,
113                                               part->addr, last_addr);
114                                 return -EINVAL;
115                         }
116                 }
117                 if (part->addr < first_addr) {
118                         stm32prog_err("%s (0x%x): invalid address 0x%llx (min=0x%llx)",
119                                       part->name, part->id,
120                                       part->addr, first_addr);
121                         return -EINVAL;
122                 }
123
124                 pr_debug("%02d : %1d %02x %14s %02d %02d.%02d %08llx %08llx",
125                          part->part_id, part->option, part->id, part->name,
126                          part->part_type, part->target,
127                          part->dev_id, part->addr, part->size);
128         }
129         return 0;
130 }
131
132 static int treat_partition_list(struct stm32prog_data *data)
133 {
134         int i, j;
135         struct stm32prog_part_t *part;
136
137         for (j = 0; j < STM32PROG_MAX_DEV; j++) {
138                 data->dev[j].target = STM32PROG_NONE;
139                 INIT_LIST_HEAD(&data->dev[j].part_list);
140         }
141
142         for (i = 0; i < data->part_nb; i++) {
143                 part = &data->part_array[i];
144                 part->alt_id = -1;
145
146                 /* skip partition with IP="none" */
147                 if (part->target == STM32PROG_NONE) {
148                         if (IS_SELECT(part)) {
149                                 stm32prog_err("Layout: selected none phase = 0x%x",
150                                               part->id);
151                                 return -EINVAL;
152                         }
153                         continue;
154                 }
155
156                 if (part->id == PHASE_FLASHLAYOUT ||
157                     part->id > PHASE_LAST_USER) {
158                         stm32prog_err("Layout: invalid phase = 0x%x",
159                                       part->id);
160                         return -EINVAL;
161                 }
162                 for (j = i + 1; j < data->part_nb; j++) {
163                         if (part->id == data->part_array[j].id) {
164                                 stm32prog_err("Layout: duplicated phase 0x%x at line %d and %d",
165                                               part->id, i, j);
166                                 return -EINVAL;
167                         }
168                 }
169                 for (j = 0; j < STM32PROG_MAX_DEV; j++) {
170                         if (data->dev[j].target == STM32PROG_NONE) {
171                                 /* new device found */
172                                 data->dev[j].target = part->target;
173                                 data->dev[j].dev_id = part->dev_id;
174                                 data->dev_nb++;
175                                 break;
176                         } else if ((part->target == data->dev[j].target) &&
177                                    (part->dev_id == data->dev[j].dev_id)) {
178                                 break;
179                         }
180                 }
181                 if (j == STM32PROG_MAX_DEV) {
182                         stm32prog_err("Layout: too many device");
183                         return -EINVAL;
184                 }
185                 part->dev = &data->dev[j];
186                 list_add_tail(&part->list, &data->dev[j].part_list);
187         }
188
189         return 0;
190 }
191
192 static int stm32prog_alt_add(struct stm32prog_data *data,
193                              struct dfu_entity *dfu,
194                              struct stm32prog_part_t *part)
195 {
196         int ret = 0;
197         int offset = 0;
198         char devstr[10];
199         char dfustr[10];
200         char buf[ALT_BUF_LEN];
201         u32 size;
202         char multiplier,  type;
203
204         /* max 3 digit for sector size */
205         if (part->size > SZ_1M) {
206                 size = (u32)(part->size / SZ_1M);
207                 multiplier = 'M';
208         } else if (part->size > SZ_1K) {
209                 size = (u32)(part->size / SZ_1K);
210                 multiplier = 'K';
211         } else {
212                 size = (u32)part->size;
213                 multiplier = 'B';
214         }
215         if (IS_SELECT(part) && !IS_EMPTY(part))
216                 type = 'e'; /*Readable and Writeable*/
217         else
218                 type = 'a';/*Readable*/
219
220         memset(buf, 0, sizeof(buf));
221         offset = snprintf(buf, ALT_BUF_LEN - offset,
222                           "@%s/0x%02x/1*%d%c%c ",
223                           part->name, part->id,
224                           size, multiplier, type);
225
226         if (part->part_type == RAW_IMAGE) {
227                 u64 dfu_size;
228
229                 dfu_size = part->size;
230                 offset += snprintf(buf + offset, ALT_BUF_LEN - offset,
231                                    "raw 0x0 0x%llx", dfu_size);
232         } else {
233                 offset += snprintf(buf + offset,
234                                    ALT_BUF_LEN - offset,
235                                    "part");
236                 offset += snprintf(buf + offset, ALT_BUF_LEN - offset,
237                                    " %d;", part->part_id);
238         }
239         switch (part->target) {
240         default:
241                 stm32prog_err("invalid target: %d", part->target);
242                 return -ENODEV;
243         }
244         pr_debug("dfu_alt_add(%s,%s,%s)\n", dfustr, devstr, buf);
245         ret = dfu_alt_add(dfu, dfustr, devstr, buf);
246         pr_debug("dfu_alt_add(%s,%s,%s) result %d\n",
247                  dfustr, devstr, buf, ret);
248
249         return ret;
250 }
251
252 static int stm32prog_alt_add_virt(struct dfu_entity *dfu,
253                                   char *name, int phase, int size)
254 {
255         int ret = 0;
256         char devstr[4];
257         char buf[ALT_BUF_LEN];
258
259         sprintf(devstr, "%d", phase);
260         sprintf(buf, "@%s/0x%02x/1*%dBe", name, phase, size);
261         ret = dfu_alt_add(dfu, "virt", devstr, buf);
262         pr_debug("dfu_alt_add(virt,%s,%s) result %d\n", devstr, buf, ret);
263
264         return ret;
265 }
266
267 static int dfu_init_entities(struct stm32prog_data *data)
268 {
269         int ret = 0;
270         int phase, i, alt_id;
271         struct stm32prog_part_t *part;
272         struct dfu_entity *dfu;
273         int alt_nb;
274
275         alt_nb = 1; /* number of virtual = CMD */
276         if (data->part_nb == 0)
277                 alt_nb++;  /* +1 for FlashLayout */
278         else
279                 for (i = 0; i < data->part_nb; i++) {
280                         if (data->part_array[i].target != STM32PROG_NONE)
281                                 alt_nb++;
282                 }
283
284         if (dfu_alt_init(alt_nb, &dfu))
285                 return -ENODEV;
286
287         puts("DFU alt info setting: ");
288         if (data->part_nb) {
289                 alt_id = 0;
290                 for (phase = 1;
291                      (phase <= PHASE_LAST_USER) &&
292                      (alt_id < alt_nb) && !ret;
293                      phase++) {
294                         /* ordering alt setting by phase id */
295                         part = NULL;
296                         for (i = 0; i < data->part_nb; i++) {
297                                 if (phase == data->part_array[i].id) {
298                                         part = &data->part_array[i];
299                                         break;
300                                 }
301                         }
302                         if (!part)
303                                 continue;
304                         if (part->target == STM32PROG_NONE)
305                                 continue;
306                         part->alt_id = alt_id;
307                         alt_id++;
308
309                         ret = stm32prog_alt_add(data, dfu, part);
310                 }
311         } else {
312                 char buf[ALT_BUF_LEN];
313
314                 sprintf(buf, "@FlashLayout/0x%02x/1*256Ke ram %x 40000",
315                         PHASE_FLASHLAYOUT, STM32_DDR_BASE);
316                 ret = dfu_alt_add(dfu, "ram", NULL, buf);
317                 pr_debug("dfu_alt_add(ram, NULL,%s) result %d\n", buf, ret);
318         }
319
320         if (!ret)
321                 ret = stm32prog_alt_add_virt(dfu, "virtual", PHASE_CMD, 512);
322
323         if (ret)
324                 stm32prog_err("dfu init failed: %d", ret);
325         puts("done\n");
326
327 #ifdef DEBUG
328         dfu_show_entities();
329 #endif
330         return ret;
331 }
332
333 static void stm32prog_end_phase(struct stm32prog_data *data)
334 {
335         if (data->phase == PHASE_FLASHLAYOUT) {
336                 if (parse_flash_layout(data, STM32_DDR_BASE, 0))
337                         stm32prog_err("Layout: invalid FlashLayout");
338                 return;
339         }
340
341         if (!data->cur_part)
342                 return;
343 }
344
345 void stm32prog_do_reset(struct stm32prog_data *data)
346 {
347         if (data->phase == PHASE_RESET) {
348                 data->phase = PHASE_DO_RESET;
349                 puts("Reset requested\n");
350         }
351 }
352
353 void stm32prog_next_phase(struct stm32prog_data *data)
354 {
355         int phase, i;
356         struct stm32prog_part_t *part;
357         bool found;
358
359         phase = data->phase;
360         switch (phase) {
361         case PHASE_RESET:
362         case PHASE_END:
363         case PHASE_DO_RESET:
364                 return;
365         }
366
367         /* found next selected partition */
368         data->cur_part = NULL;
369         data->phase = PHASE_END;
370         found = false;
371         do {
372                 phase++;
373                 if (phase > PHASE_LAST_USER)
374                         break;
375                 for (i = 0; i < data->part_nb; i++) {
376                         part = &data->part_array[i];
377                         if (part->id == phase) {
378                                 if (IS_SELECT(part) && !IS_EMPTY(part)) {
379                                         data->cur_part = part;
380                                         data->phase = phase;
381                                         found = true;
382                                 }
383                                 break;
384                         }
385                 }
386         } while (!found);
387
388         if (data->phase == PHASE_END)
389                 puts("Phase=END\n");
390 }
391
392 static void stm32prog_devices_init(struct stm32prog_data *data)
393 {
394         int i;
395         int ret;
396
397         ret = treat_partition_list(data);
398         if (ret)
399                 goto error;
400
401         /* initialize the selected device */
402         for (i = 0; i < data->dev_nb; i++) {
403                 ret = init_device(data, &data->dev[i]);
404                 if (ret)
405                         goto error;
406         }
407
408         return;
409
410 error:
411         data->part_nb = 0;
412 }
413
414 int stm32prog_dfu_init(struct stm32prog_data *data)
415 {
416         /* init device if no error */
417         if (data->part_nb)
418                 stm32prog_devices_init(data);
419
420         if (data->part_nb)
421                 stm32prog_next_phase(data);
422
423         /* prepare DFU for device read/write */
424         dfu_free_entities();
425         return dfu_init_entities(data);
426 }
427
428 int stm32prog_init(struct stm32prog_data *data, ulong addr, ulong size)
429 {
430         memset(data, 0x0, sizeof(*data));
431         data->phase = PHASE_FLASHLAYOUT;
432
433         return parse_flash_layout(data, addr, size);
434 }
435
436 void stm32prog_clean(struct stm32prog_data *data)
437 {
438         /* clean */
439         dfu_free_entities();
440         free(data->part_array);
441         free(data->header_data);
442 }
443
444 /* DFU callback: used after serial and direct DFU USB access */
445 void dfu_flush_callback(struct dfu_entity *dfu)
446 {
447         if (!stm32prog_data)
448                 return;
449
450         if (dfu->dev_type == DFU_DEV_RAM) {
451                 if (dfu->alt == 0 &&
452                     stm32prog_data->phase == PHASE_FLASHLAYOUT) {
453                         stm32prog_end_phase(stm32prog_data);
454                         /* waiting DFU DETACH for reenumeration */
455                 }
456         }
457
458         if (!stm32prog_data->cur_part)
459                 return;
460
461         if (dfu->alt == stm32prog_data->cur_part->alt_id) {
462                 stm32prog_end_phase(stm32prog_data);
463                 stm32prog_next_phase(stm32prog_data);
464         }
465 }
466
467 void dfu_initiated_callback(struct dfu_entity *dfu)
468 {
469         if (!stm32prog_data)
470                 return;
471
472         if (!stm32prog_data->cur_part)
473                 return;
474
475         /* force the saved offset for the current partition */
476         if (dfu->alt == stm32prog_data->cur_part->alt_id) {
477                 dfu->offset = stm32prog_data->offset;
478                 pr_debug("dfu offset = 0x%llx\n", dfu->offset);
479         }
480 }