test: stabilize test_efi_secboot
[oweals/u-boot.git] / drivers / dfu / dfu_nand.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * dfu_nand.c -- DFU for NAND routines.
4  *
5  * Copyright (C) 2012-2013 Texas Instruments, Inc.
6  *
7  * Based on dfu_mmc.c which is:
8  * Copyright (C) 2012 Samsung Electronics
9  * author: Lukasz Majewski <l.majewski@samsung.com>
10  */
11
12 #include <common.h>
13 #include <malloc.h>
14 #include <errno.h>
15 #include <div64.h>
16 #include <dfu.h>
17 #include <linux/mtd/mtd.h>
18 #include <jffs2/load_kernel.h>
19 #include <nand.h>
20
21 static int nand_block_op(enum dfu_op op, struct dfu_entity *dfu,
22                         u64 offset, void *buf, long *len)
23 {
24         loff_t start, lim;
25         size_t count, actual;
26         int ret;
27         struct mtd_info *mtd;
28
29         /* if buf == NULL return total size of the area */
30         if (buf == NULL) {
31                 *len = dfu->data.nand.size;
32                 return 0;
33         }
34
35         start = dfu->data.nand.start + offset + dfu->bad_skip;
36         lim = dfu->data.nand.start + dfu->data.nand.size - start;
37         count = *len;
38
39         mtd = get_nand_dev_by_index(nand_curr_device);
40
41         if (nand_curr_device < 0 ||
42             nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE ||
43             !mtd) {
44                 printf("%s: invalid nand device\n", __func__);
45                 return -1;
46         }
47
48         if (op == DFU_OP_READ) {
49                 ret = nand_read_skip_bad(mtd, start, &count, &actual,
50                                          lim, buf);
51         } else {
52                 nand_erase_options_t opts;
53                 int write_flags = WITH_WR_VERIFY;
54
55                 memset(&opts, 0, sizeof(opts));
56                 opts.offset = start;
57                 opts.length = count;
58                 opts.spread = 1;
59                 opts.quiet = 1;
60                 opts.lim = lim;
61                 /* first erase */
62                 ret = nand_erase_opts(mtd, &opts);
63                 if (ret)
64                         return ret;
65                 /* then write */
66 #ifdef CONFIG_DFU_NAND_TRIMFFS
67                 if (dfu->data.nand.ubi)
68                         write_flags |= WITH_DROP_FFS;
69 #endif
70                 ret = nand_write_skip_bad(mtd, start, &count, &actual,
71                                           lim, buf, write_flags);
72         }
73
74         if (ret != 0) {
75                 printf("%s: nand_%s_skip_bad call failed at %llx!\n",
76                        __func__, op == DFU_OP_READ ? "read" : "write",
77                        start);
78                 return ret;
79         }
80
81         /*
82          * Find out where we stopped writing data.  This can be deeper into
83          * the NAND than we expected due to having to skip bad blocks.  So
84          * we must take this into account for the next write, if any.
85          */
86         if (actual > count)
87                 dfu->bad_skip += actual - count;
88
89         return ret;
90 }
91
92 static inline int nand_block_write(struct dfu_entity *dfu,
93                 u64 offset, void *buf, long *len)
94 {
95         return nand_block_op(DFU_OP_WRITE, dfu, offset, buf, len);
96 }
97
98 static inline int nand_block_read(struct dfu_entity *dfu,
99                 u64 offset, void *buf, long *len)
100 {
101         return nand_block_op(DFU_OP_READ, dfu, offset, buf, len);
102 }
103
104 static int dfu_write_medium_nand(struct dfu_entity *dfu,
105                 u64 offset, void *buf, long *len)
106 {
107         int ret = -1;
108
109         switch (dfu->layout) {
110         case DFU_RAW_ADDR:
111                 ret = nand_block_write(dfu, offset, buf, len);
112                 break;
113         default:
114                 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
115                        dfu_get_layout(dfu->layout));
116         }
117
118         return ret;
119 }
120
121 int dfu_get_medium_size_nand(struct dfu_entity *dfu, u64 *size)
122 {
123         *size = dfu->data.nand.size;
124
125         return 0;
126 }
127
128 static int dfu_read_medium_nand(struct dfu_entity *dfu, u64 offset, void *buf,
129                 long *len)
130 {
131         int ret = -1;
132
133         switch (dfu->layout) {
134         case DFU_RAW_ADDR:
135                 ret = nand_block_read(dfu, offset, buf, len);
136                 break;
137         default:
138                 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
139                        dfu_get_layout(dfu->layout));
140         }
141
142         return ret;
143 }
144
145 static int dfu_flush_medium_nand(struct dfu_entity *dfu)
146 {
147         int ret = 0;
148         u64 off;
149
150         /* in case of ubi partition, erase rest of the partition */
151         if (dfu->data.nand.ubi) {
152                 struct mtd_info *mtd = get_nand_dev_by_index(nand_curr_device);
153                 nand_erase_options_t opts;
154
155                 if (nand_curr_device < 0 ||
156                     nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE ||
157                     !mtd) {
158                         printf("%s: invalid nand device\n", __func__);
159                         return -1;
160                 }
161
162                 memset(&opts, 0, sizeof(opts));
163                 off = dfu->offset;
164                 if ((off & (mtd->erasesize - 1)) != 0) {
165                         /*
166                          * last write ended with unaligned length
167                          * sector is erased, jump to next
168                          */
169                         off = off & ~((mtd->erasesize - 1));
170                         off += mtd->erasesize;
171                 }
172                 opts.offset = dfu->data.nand.start + off +
173                                 dfu->bad_skip;
174                 opts.length = dfu->data.nand.start +
175                                 dfu->data.nand.size - opts.offset;
176                 ret = nand_erase_opts(mtd, &opts);
177                 if (ret != 0)
178                         printf("Failure erase: %d\n", ret);
179         }
180
181         return ret;
182 }
183
184 unsigned int dfu_polltimeout_nand(struct dfu_entity *dfu)
185 {
186         /*
187          * Currently, Poll Timeout != 0 is only needed on nand
188          * ubi partition, as the not used sectors need an erase
189          */
190         if (dfu->data.nand.ubi)
191                 return DFU_MANIFEST_POLL_TIMEOUT;
192
193         return DFU_DEFAULT_POLL_TIMEOUT;
194 }
195
196 int dfu_fill_entity_nand(struct dfu_entity *dfu, char *devstr, char *s)
197 {
198         char *st;
199         int ret, dev, part;
200
201         dfu->data.nand.ubi = 0;
202         dfu->dev_type = DFU_DEV_NAND;
203         st = strsep(&s, " ");
204         if (!strcmp(st, "raw")) {
205                 dfu->layout = DFU_RAW_ADDR;
206                 dfu->data.nand.start = simple_strtoul(s, &s, 16);
207                 s++;
208                 dfu->data.nand.size = simple_strtoul(s, &s, 16);
209         } else if ((!strcmp(st, "part")) || (!strcmp(st, "partubi"))) {
210                 char mtd_id[32];
211                 struct mtd_device *mtd_dev;
212                 u8 part_num;
213                 struct part_info *pi;
214
215                 dfu->layout = DFU_RAW_ADDR;
216
217                 dev = simple_strtoul(s, &s, 10);
218                 s++;
219                 part = simple_strtoul(s, &s, 10);
220
221                 sprintf(mtd_id, "%s%d,%d", "nand", dev, part - 1);
222                 debug("using id '%s'\n", mtd_id);
223
224                 mtdparts_init();
225
226                 ret = find_dev_and_part(mtd_id, &mtd_dev, &part_num, &pi);
227                 if (ret != 0) {
228                         printf("Could not locate '%s'\n", mtd_id);
229                         return -1;
230                 }
231
232                 dfu->data.nand.start = pi->offset;
233                 dfu->data.nand.size = pi->size;
234                 if (!strcmp(st, "partubi"))
235                         dfu->data.nand.ubi = 1;
236         } else {
237                 printf("%s: Memory layout (%s) not supported!\n", __func__, st);
238                 return -1;
239         }
240
241         dfu->get_medium_size = dfu_get_medium_size_nand;
242         dfu->read_medium = dfu_read_medium_nand;
243         dfu->write_medium = dfu_write_medium_nand;
244         dfu->flush_medium = dfu_flush_medium_nand;
245         dfu->poll_timeout = dfu_polltimeout_nand;
246
247         /* initial state */
248         dfu->inited = 0;
249
250         return 0;
251 }