toradex: configblock: move environment config to common
[u-boot.git] / board / toradex / common / configblock.c
1 /*
2  * Copyright (c) 2011-2015 Toradex, Inc.
3  *
4  * SPDX-License-Identifier:     GPL-2.0+
5  */
6
7 #include "configblock.h"
8 #include <common.h>
9
10 #include <cli.h>
11 #include <malloc.h>
12 #include <mmc.h>
13 #include <nand.h>
14
15 DECLARE_GLOBAL_DATA_PTR;
16
17 #define TAG_VALID       0xcf01
18 #define TAG_MAC         0x0000
19 #define TAG_HW          0x0008
20
21 #define TAG_FLAG_VALID  0x1
22
23 #if defined(CONFIG_TRDX_CFG_BLOCK_IS_IN_MMC)
24 #define TRDX_CFG_BLOCK_MAX_SIZE 512
25 #elif defined(CONFIG_TRDX_CFG_BLOCK_IS_IN_NAND)
26 #define TRDX_CFG_BLOCK_MAX_SIZE 64
27 #else
28 #error Toradex config block location not set
29 #endif
30
31 struct toradex_tag {
32         u32 len : 14;
33         u32 flags : 2;
34         u32 id : 16;
35 };
36
37 bool valid_cfgblock;
38 struct toradex_hw trdx_hw_tag;
39 struct toradex_eth_addr trdx_eth_addr;
40 u32 trdx_serial;
41
42 const char* const toradex_modules[] = {
43          [1] = "Colibri PXA270 312MHz",
44          [2] = "Colibri PXA270 520MHz",
45          [3] = "Colibri PXA320 806MHz",
46          [4] = "Colibri PXA300 208MHz",
47          [5] = "Colibri PXA310 624MHz",
48          [6] = "Colibri PXA320 806MHz IT",
49          [7] = "Colibri PXA300 208MHz XT",
50          [8] = "Colibri PXA270 312MHz",
51          [9] = "Colibri PXA270 520MHz",
52         [10] = "Colibri VF50 128MB", /* not currently on sale */
53         [11] = "Colibri VF61 256MB",
54         [12] = "Colibri VF61 256MB IT",
55         [13] = "Colibri VF50 128MB IT",
56         [14] = "Colibri iMX6 Solo 256MB",
57         [15] = "Colibri iMX6 DualLite 512MB",
58         [16] = "Colibri iMX6 Solo 256MB IT",
59         [17] = "Colibri iMX6 DualLite 512MB IT",
60         [20] = "Colibri T20 256MB",
61         [21] = "Colibri T20 512MB",
62         [22] = "Colibri T20 512MB IT",
63         [23] = "Colibri T30 1GB",
64         [24] = "Colibri T20 256MB IT",
65         [25] = "Apalis T30 2GB",
66         [26] = "Apalis T30 1GB",
67         [27] = "Apalis iMX6 Quad 1GB",
68         [28] = "Apalis iMX6 Quad 2GB IT",
69         [29] = "Apalis iMX6 Dual 512MB",
70         [30] = "Colibri T30 1GB IT",
71         [31] = "Apalis T30 1GB IT",
72 };
73
74 #ifdef CONFIG_TRDX_CFG_BLOCK_IS_IN_MMC
75 static int trdx_cfg_block_mmc_storage(u8 *config_block, int write)
76 {
77         struct mmc *mmc;
78         int dev = CONFIG_TRDX_CFG_BLOCK_DEV;
79         int offset = CONFIG_TRDX_CFG_BLOCK_OFFSET;
80         uint part = CONFIG_TRDX_CFG_BLOCK_PART;
81         uint blk_start;
82         int ret = 0;
83
84         /* Read production parameter config block from eMMC */
85         mmc = find_mmc_device(dev);
86         if (!mmc) {
87                 puts("No MMC card found\n");
88                 ret = -ENODEV;
89                 goto out;
90         }
91         if (part != mmc->part_num) {
92                 if (mmc_switch_part(dev, part)) {
93                         puts("MMC partition switch failed\n");
94                         ret = -ENODEV;
95                         goto out;
96                 }
97         }
98         if (offset < 0)
99                 offset += mmc->capacity;
100         blk_start = ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len;
101
102         if (!write) {
103                 /* Careful reads a whole block of 512 bytes into config_block */
104                 if (mmc->block_dev.block_read(dev, blk_start, 1,
105                                               (unsigned char *)config_block) !=
106                     1) {
107                         ret = -EIO;
108                         goto out;
109                 }
110         } else {
111                 /* Just writing one 512 byte block */
112                 if (mmc->block_dev.block_write(dev, blk_start, 1,
113                                                (unsigned char *)config_block) !=
114                     1) {
115                         ret = -EIO;
116                         goto out;
117                 }
118         }
119
120 out:
121         /* Switch back to regular eMMC user partition */
122         mmc_switch_part(0, 0);
123
124         return ret;
125 }
126 #endif
127
128 #ifdef CONFIG_TRDX_CFG_BLOCK_IS_IN_NAND
129 static int read_trdx_cfg_block_from_nand(unsigned char *config_block)
130 {
131         size_t size = TRDX_CFG_BLOCK_MAX_SIZE;
132
133         /* Read production parameter config block from NAND page */
134         return nand_read(&nand_info[0], CONFIG_TRDX_CFG_BLOCK_OFFSET,
135                          &size, config_block);
136 }
137
138 static int write_trdx_cfg_block_to_nand(unsigned char *config_block)
139 {
140         size_t size = TRDX_CFG_BLOCK_MAX_SIZE;
141
142         /* Write production parameter config block to NAND page */
143         return nand_write(&nand_info[0], CONFIG_TRDX_CFG_BLOCK_OFFSET,
144                           &size, config_block);
145 }
146 #endif
147
148 int read_trdx_cfg_block(void)
149 {
150         int ret = 0;
151         u8 *config_block = NULL;
152         struct toradex_tag *tag;
153         size_t size = TRDX_CFG_BLOCK_MAX_SIZE;
154         int offset;
155
156         /* Allocate RAM area for config block */
157         config_block = memalign(ARCH_DMA_MINALIGN, size);
158         if (!config_block) {
159                 printf("Not enough malloc space available!\n");
160                 return -ENOMEM;
161         }
162
163         memset(config_block, 0, size);
164
165 #if defined(CONFIG_TRDX_CFG_BLOCK_IS_IN_MMC)
166         ret = trdx_cfg_block_mmc_storage(config_block, 0);
167 #elif defined(CONFIG_TRDX_CFG_BLOCK_IS_IN_NAND)
168         ret = read_trdx_cfg_block_from_nand(config_block);
169 #else
170         ret = -EINVAL;
171 #endif
172         if (ret)
173                 goto out;
174
175         /* Expect a valid tag first */
176         tag = (struct toradex_tag *)config_block;
177         if (tag->flags != TAG_FLAG_VALID || tag->id != TAG_VALID) {
178                 valid_cfgblock = false;
179                 ret = -EINVAL;
180                 goto out;
181         }
182         valid_cfgblock = true;
183         offset = 4;
184
185         while (true) {
186                 tag = (struct toradex_tag *)(config_block + offset);
187                 offset += 4;
188                 if (tag->flags != TAG_FLAG_VALID)
189                         break;
190
191                 switch (tag->id)
192                 {
193                 case TAG_MAC:
194                         memcpy(&trdx_eth_addr, config_block + offset, 6);
195
196                         /* NIC part of MAC address is serial number */
197                         trdx_serial = ntohl(trdx_eth_addr.nic) >> 8;
198                         break;
199                 case TAG_HW:
200                         memcpy(&trdx_hw_tag, config_block + offset, 8);
201                         break;
202                 }
203
204                 /* Get to next tag according to current tags length */
205                 offset += tag->len * 4;
206         }
207
208 out:
209         free(config_block);
210         return ret;
211 }
212
213 static int get_cfgblock_interactive(void)
214 {
215         char message[CONFIG_SYS_CBSIZE];
216         char *soc;
217         char it = 'n';
218         int len;
219
220         sprintf(message, "Is the module an IT version? [y/N] ");
221         len = cli_readline(message);
222         it = console_buffer[0];
223
224         soc = getenv("soc");
225         if (!strcmp("mx6", soc)) {
226 #ifdef CONFIG_MACH_TYPE
227                 if (it == 'y' || it == 'Y')
228                         trdx_hw_tag.prodid = APALIS_IMX6Q_IT;
229                 else
230                         if (gd->ram_size == 0x40000000)
231                                 trdx_hw_tag.prodid = APALIS_IMX6Q;
232                         else
233                                 trdx_hw_tag.prodid = APALIS_IMX6D;
234 #else
235                 if (it == 'y' || it == 'Y')
236                         if (gd->ram_size == 0x20000000)
237                                 trdx_hw_tag.prodid = COLIBRI_IMX6DL_IT;
238                         else
239                                 trdx_hw_tag.prodid = COLIBRI_IMX6S_IT;
240                 else
241                         if (gd->ram_size == 0x20000000)
242                                 trdx_hw_tag.prodid = COLIBRI_IMX6DL;
243                         else
244                                 trdx_hw_tag.prodid = COLIBRI_IMX6S;
245 #endif /* CONFIG_MACH_TYPE */
246         } else if (!strcmp("tegra20", soc)) {
247                 if (it == 'y' || it == 'Y')
248                         if (gd->ram_size == 0x10000000)
249                                 trdx_hw_tag.prodid = COLIBRI_T20_256MB_IT;
250                         else
251                                 trdx_hw_tag.prodid = COLIBRI_T20_512MB_IT;
252                 else
253                         if (gd->ram_size == 0x10000000)
254                                 trdx_hw_tag.prodid = COLIBRI_T20_256MB;
255                         else
256                                 trdx_hw_tag.prodid = COLIBRI_T20_512MB;
257 #ifdef CONFIG_MACH_TYPE
258         } else if (!strcmp("tegra30", soc)) {
259                 if (CONFIG_MACH_TYPE == MACH_TYPE_APALIS_T30) {
260                         if (it == 'y' || it == 'Y')
261                                 trdx_hw_tag.prodid = APALIS_T30_IT;
262                         else
263                                 if (gd->ram_size == 0x40000000)
264                                         trdx_hw_tag.prodid = APALIS_T30_1GB;
265                                 else
266                                         trdx_hw_tag.prodid = APALIS_T30_2GB;
267                 } else {
268                         if (it == 'y' || it == 'Y')
269                                 trdx_hw_tag.prodid = COLIBRI_T30_IT;
270                         else
271                                 trdx_hw_tag.prodid = COLIBRI_T30;
272                 }
273 #endif /* CONFIG_MACH_TYPE */
274         } else if (!strcmp("vf500", soc)) {
275                 if (it == 'y' || it == 'Y')
276                         trdx_hw_tag.prodid = COLIBRI_VF50_IT;
277                 else
278                         trdx_hw_tag.prodid = COLIBRI_VF50;
279         } else if (!strcmp("vf610", soc)) {
280                 if (it == 'y' || it == 'Y')
281                         trdx_hw_tag.prodid = COLIBRI_VF61_IT;
282                 else
283                         trdx_hw_tag.prodid = COLIBRI_VF61;
284         } else {
285                 printf("Module type not detectable due to unknown SoC\n");
286                 return -1;
287         }
288
289         while (len < 4) {
290                 sprintf(message, "Enter the module version (e.g. V1.1B): V");
291                 len = cli_readline(message);
292         }
293
294         trdx_hw_tag.ver_major = console_buffer[0] - '0';
295         trdx_hw_tag.ver_minor = console_buffer[2] - '0';
296         trdx_hw_tag.ver_assembly = console_buffer[3] - 'A';
297
298         while (len < 8) {
299                 sprintf(message, "Enter module serial number: ");
300                 len = cli_readline(message);
301         }
302
303         trdx_serial = simple_strtoul(console_buffer, NULL, 10);
304
305         return 0;
306 }
307
308 static int get_cfgblock_barcode(char *barcode)
309 {
310         if (strlen(barcode) < 16) {
311                 printf("Argument too short, barcode is 16 chars long\n");
312                 return -1;
313         }
314
315         /* Get hardware information from the first 8 digits */
316         trdx_hw_tag.ver_major = barcode[4] - '0';
317         trdx_hw_tag.ver_minor = barcode[5] - '0';
318         trdx_hw_tag.ver_assembly = barcode[7] - '0';
319
320         barcode[4] = '\0';
321         trdx_hw_tag.prodid = simple_strtoul(barcode, NULL, 10);
322
323         /* Parse second part of the barcode (serial number */
324         barcode += 8;
325         trdx_serial = simple_strtoul(barcode, NULL, 10);
326
327         return 0;
328 }
329
330 static int do_cfgblock_create(cmd_tbl_t *cmdtp, int flag, int argc,
331                               char * const argv[])
332 {
333         u8 *config_block;
334         struct toradex_tag *tag;
335         size_t size = TRDX_CFG_BLOCK_MAX_SIZE;
336         int offset = 0;
337         int ret;
338
339         /* Allocate RAM area for config block */
340         config_block = memalign(ARCH_DMA_MINALIGN, size);
341         if (!config_block) {
342                 printf("Not enough malloc space available!\n");
343                 return -ENOMEM;
344         }
345
346         memset(config_block, 0xff, size);
347
348         read_trdx_cfg_block();
349         if (valid_cfgblock) {
350 #ifdef CONFIG_TRDX_CFG_BLOCK_IS_IN_NAND
351                 /*
352                  * On NAND devices, recreation is only allowed if the page is
353                  * empty (config block invalid...)
354                  */
355                 printf("NAND erase block %d need to be erased before creating "
356                        "a Toradex config block\n",
357                        CONFIG_TRDX_CFG_BLOCK_OFFSET / nand_info[0].erasesize);
358                 ret = 0;
359                 goto out;
360 #else
361                 char message[CONFIG_SYS_CBSIZE];
362                 sprintf(message, "A valid Toradex config block is present, "
363                                  "still recreate? [y/N] ");
364
365                 if (!cli_readline(message)) {
366                         ret = 0;
367                         goto out;
368                 }
369
370                 if (console_buffer[0] != 'y' && console_buffer[0] != 'Y') {
371                         ret = 0;
372                         goto out;
373                 }
374 #endif
375         }
376
377         /* Parse new Toradex config  block data... */
378         if (argc < 3)
379                 ret = get_cfgblock_interactive();
380         else
381                 ret = get_cfgblock_barcode(argv[2]);
382
383         if (ret)
384                 goto out;
385
386         /* Convert serial number to MAC address (the storage format) */
387         trdx_eth_addr.oui = htonl(0x00142dUL << 8);
388         trdx_eth_addr.nic = htonl(trdx_serial << 8);
389
390         /* Valid Tag */
391         tag = (struct toradex_tag *)config_block;
392         tag->id = TAG_VALID;
393         tag->flags = TAG_FLAG_VALID;
394         tag->len = 0;
395         offset += 4;
396
397         /* Product Tag */
398         tag = (struct toradex_tag *)(config_block + offset);
399         tag->id = TAG_HW;
400         tag->flags = TAG_FLAG_VALID;
401         tag->len = 2;
402         offset += 4;
403
404         memcpy(config_block + offset, &trdx_hw_tag, 8);
405         offset += 8;
406
407         /* MAC Tag */
408         tag = (struct toradex_tag *)(config_block + offset);
409         tag->id = TAG_MAC;
410         tag->flags = TAG_FLAG_VALID;
411         tag->len = 2;
412         offset += 4;
413
414         memcpy(config_block + offset, &trdx_eth_addr, 6);
415         offset +=6;
416         memset(config_block + offset, 0, 32 - offset);
417
418 #ifdef CONFIG_TRDX_CFG_BLOCK_IS_IN_MMC
419         ret = trdx_cfg_block_mmc_storage(config_block, 1);
420 #elif defined(CONFIG_TRDX_CFG_BLOCK_IS_IN_NAND)
421         ret = write_trdx_cfg_block_to_nand(config_block);
422 #else
423         ret = -EINVAL;
424 #endif
425         if (ret) {
426                 printf("Failed to write Toradex config block: %d\n", ret);
427                 goto out;
428         }
429
430         printf("Toradex config block successfully written\n");
431
432 out:
433         free(config_block);
434         return ret;
435 }
436
437 static int do_cfgblock(cmd_tbl_t *cmdtp, int flag, int argc,
438                        char * const argv[])
439 {
440         int ret;
441
442         if (argc < 2)
443                 return CMD_RET_USAGE;
444
445         if (!strcmp(argv[1], "create")) {
446                 return do_cfgblock_create(cmdtp, flag, argc, argv);
447         } else if (!strcmp(argv[1], "reload")) {
448                 ret = read_trdx_cfg_block();
449                 if (ret)
450                         printf("Failed to reload Toradex config block: %d\n",
451                                ret);
452                 return 0;
453         }
454
455         return CMD_RET_USAGE;
456 }
457
458 U_BOOT_CMD(
459         cfgblock, 3, 0, do_cfgblock,
460         "Toradex config block handling commands",
461         "create [barcode] - (Re-)create Toradex config block\n"
462         "cfgblock reload - Reload Toradex config block from flash"
463 );