2bfbd698304ceda6d3ec09a52744f7a3fa7240b4
[fw/openocd] / src / flash / nand / fileio.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2
3 /***************************************************************************
4  *   Copyright (C) 2007 by Dominic Rath <Dominic.Rath@gmx.de>              *
5  *   Copyright (C) 2002 Thomas Gleixner <tglx@linutronix.de>               *
6  *   Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net>             *
7  *                                                                         *
8  *   Partially based on drivers/mtd/nand_ids.c from Linux.                 *
9  ***************************************************************************/
10
11 #ifdef HAVE_CONFIG_H
12 #include "config.h"
13 #endif
14
15 #include "core.h"
16 #include "fileio.h"
17
18 static struct nand_ecclayout nand_oob_16 = {
19         .eccbytes = 6,
20         .eccpos = {0, 1, 2, 3, 6, 7},
21         .oobfree = {
22                 {.offset = 8,
23                  .length = 8}
24         }
25 };
26
27 static struct nand_ecclayout nand_oob_64 = {
28         .eccbytes = 24,
29         .eccpos = {
30                 40, 41, 42, 43, 44, 45, 46, 47,
31                 48, 49, 50, 51, 52, 53, 54, 55,
32                 56, 57, 58, 59, 60, 61, 62, 63
33         },
34         .oobfree = {
35                 {.offset = 2,
36                  .length = 38}
37         }
38 };
39
40 void nand_fileio_init(struct nand_fileio_state *state)
41 {
42         memset(state, 0, sizeof(*state));
43         state->oob_format = NAND_OOB_NONE;
44 }
45
46 int nand_fileio_start(struct command_invocation *cmd,
47         struct nand_device *nand, const char *filename, int filemode,
48         struct nand_fileio_state *state)
49 {
50         if (state->address % nand->page_size) {
51                 command_print(cmd, "only page-aligned addresses are supported");
52                 return ERROR_COMMAND_SYNTAX_ERROR;
53         }
54
55         duration_start(&state->bench);
56
57         if (filename) {
58                 int retval = fileio_open(&state->fileio, filename, filemode, FILEIO_BINARY);
59                 if (retval != ERROR_OK) {
60                         const char *msg = (filemode == FILEIO_READ) ? "read" : "write";
61                         command_print(cmd, "failed to open '%s' for %s access",
62                                 filename, msg);
63                         return retval;
64                 }
65                 state->file_opened = true;
66         }
67
68         if (!(state->oob_format & NAND_OOB_ONLY)) {
69                 state->page_size = nand->page_size;
70                 state->page = malloc(nand->page_size);
71         }
72
73         if (state->oob_format & (NAND_OOB_RAW | NAND_OOB_SW_ECC | NAND_OOB_SW_ECC_KW)) {
74                 if (nand->page_size == 512) {
75                         state->oob_size = 16;
76                         state->eccpos = nand_oob_16.eccpos;
77                 } else if (nand->page_size == 2048)   {
78                         state->oob_size = 64;
79                         state->eccpos = nand_oob_64.eccpos;
80                 }
81                 state->oob = malloc(state->oob_size);
82         }
83
84         return ERROR_OK;
85 }
86 int nand_fileio_cleanup(struct nand_fileio_state *state)
87 {
88         if (state->file_opened)
89                 fileio_close(state->fileio);
90
91         free(state->oob);
92         state->oob = NULL;
93
94         free(state->page);
95         state->page = NULL;
96         return ERROR_OK;
97 }
98 int nand_fileio_finish(struct nand_fileio_state *state)
99 {
100         nand_fileio_cleanup(state);
101         return duration_measure(&state->bench);
102 }
103
104 COMMAND_HELPER(nand_fileio_parse_args, struct nand_fileio_state *state,
105         struct nand_device **dev, enum fileio_access filemode,
106         bool need_size, bool sw_ecc)
107 {
108         nand_fileio_init(state);
109
110         unsigned minargs = need_size ? 4 : 3;
111         if (minargs > CMD_ARGC)
112                 return ERROR_COMMAND_SYNTAX_ERROR;
113
114         struct nand_device *nand;
115         int retval = CALL_COMMAND_HANDLER(nand_command_get_device, 0, &nand);
116         if (retval != ERROR_OK)
117                 return retval;
118
119         if (!nand->device) {
120                 command_print(CMD, "#%s: not probed", CMD_ARGV[0]);
121                 return ERROR_NAND_DEVICE_NOT_PROBED;
122         }
123
124         COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], state->address);
125         if (need_size) {
126                 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], state->size);
127                 if (state->size % nand->page_size) {
128                         command_print(CMD, "only page-aligned sizes are supported");
129                         return ERROR_COMMAND_SYNTAX_ERROR;
130                 }
131         }
132
133         if (minargs < CMD_ARGC) {
134                 for (unsigned i = minargs; i < CMD_ARGC; i++) {
135                         if (!strcmp(CMD_ARGV[i], "oob_raw"))
136                                 state->oob_format |= NAND_OOB_RAW;
137                         else if (!strcmp(CMD_ARGV[i], "oob_only"))
138                                 state->oob_format |= NAND_OOB_RAW | NAND_OOB_ONLY;
139                         else if (sw_ecc && !strcmp(CMD_ARGV[i], "oob_softecc"))
140                                 state->oob_format |= NAND_OOB_SW_ECC;
141                         else if (sw_ecc && !strcmp(CMD_ARGV[i], "oob_softecc_kw"))
142                                 state->oob_format |= NAND_OOB_SW_ECC_KW;
143                         else {
144                                 command_print(CMD, "unknown option: %s", CMD_ARGV[i]);
145                                 return ERROR_COMMAND_SYNTAX_ERROR;
146                         }
147                 }
148         }
149
150         retval = nand_fileio_start(CMD, nand, CMD_ARGV[1], filemode, state);
151         if (retval != ERROR_OK)
152                 return retval;
153
154         if (!need_size) {
155                 size_t filesize;
156                 retval = fileio_size(state->fileio, &filesize);
157                 if (retval != ERROR_OK)
158                         return retval;
159                 state->size = filesize;
160         }
161
162         *dev = nand;
163
164         return ERROR_OK;
165 }
166
167 /**
168  * @returns If no error occurred, returns number of bytes consumed;
169  * otherwise, returns a negative error code.)
170  */
171 int nand_fileio_read(struct nand_device *nand, struct nand_fileio_state *s)
172 {
173         size_t total_read = 0;
174         size_t one_read;
175
176         if (s->page) {
177                 fileio_read(s->fileio, s->page_size, s->page, &one_read);
178                 if (one_read < s->page_size)
179                         memset(s->page + one_read, 0xff, s->page_size - one_read);
180                 total_read += one_read;
181         }
182
183         if (s->oob_format & NAND_OOB_SW_ECC) {
184                 uint8_t ecc[3];
185                 memset(s->oob, 0xff, s->oob_size);
186                 for (uint32_t i = 0, j = 0; i < s->page_size; i += 256) {
187                         nand_calculate_ecc(nand, s->page + i, ecc);
188                         s->oob[s->eccpos[j++]] = ecc[0];
189                         s->oob[s->eccpos[j++]] = ecc[1];
190                         s->oob[s->eccpos[j++]] = ecc[2];
191                 }
192         } else if (s->oob_format & NAND_OOB_SW_ECC_KW)   {
193                 /*
194                  * In this case eccpos is not used as
195                  * the ECC data is always stored contiguously
196                  * at the end of the OOB area.  It consists
197                  * of 10 bytes per 512-byte data block.
198                  */
199                 uint8_t *ecc = s->oob + s->oob_size - s->page_size / 512 * 10;
200                 memset(s->oob, 0xff, s->oob_size);
201                 for (uint32_t i = 0; i < s->page_size; i += 512) {
202                         nand_calculate_ecc_kw(nand, s->page + i, ecc);
203                         ecc += 10;
204                 }
205         } else if (s->oob)   {
206                 fileio_read(s->fileio, s->oob_size, s->oob, &one_read);
207                 if (one_read < s->oob_size)
208                         memset(s->oob + one_read, 0xff, s->oob_size - one_read);
209                 total_read += one_read;
210         }
211         return total_read;
212 }