Merge git://git.denx.de/u-boot-sunxi
[platform/kernel/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
54                 memset(&opts, 0, sizeof(opts));
55                 opts.offset = start;
56                 opts.length = count;
57                 opts.spread = 1;
58                 opts.quiet = 1;
59                 opts.lim = lim;
60                 /* first erase */
61                 ret = nand_erase_opts(mtd, &opts);
62                 if (ret)
63                         return ret;
64                 /* then write */
65                 ret = nand_write_skip_bad(mtd, start, &count, &actual,
66                                           lim, buf, WITH_WR_VERIFY);
67         }
68
69         if (ret != 0) {
70                 printf("%s: nand_%s_skip_bad call failed at %llx!\n",
71                        __func__, op == DFU_OP_READ ? "read" : "write",
72                        start);
73                 return ret;
74         }
75
76         /*
77          * Find out where we stopped writing data.  This can be deeper into
78          * the NAND than we expected due to having to skip bad blocks.  So
79          * we must take this into account for the next write, if any.
80          */
81         if (actual > count)
82                 dfu->bad_skip += actual - count;
83
84         return ret;
85 }
86
87 static inline int nand_block_write(struct dfu_entity *dfu,
88                 u64 offset, void *buf, long *len)
89 {
90         return nand_block_op(DFU_OP_WRITE, dfu, offset, buf, len);
91 }
92
93 static inline int nand_block_read(struct dfu_entity *dfu,
94                 u64 offset, void *buf, long *len)
95 {
96         return nand_block_op(DFU_OP_READ, dfu, offset, buf, len);
97 }
98
99 static int dfu_write_medium_nand(struct dfu_entity *dfu,
100                 u64 offset, void *buf, long *len)
101 {
102         int ret = -1;
103
104         switch (dfu->layout) {
105         case DFU_RAW_ADDR:
106                 ret = nand_block_write(dfu, offset, buf, len);
107                 break;
108         default:
109                 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
110                        dfu_get_layout(dfu->layout));
111         }
112
113         return ret;
114 }
115
116 int dfu_get_medium_size_nand(struct dfu_entity *dfu, u64 *size)
117 {
118         *size = dfu->data.nand.size;
119
120         return 0;
121 }
122
123 static int dfu_read_medium_nand(struct dfu_entity *dfu, u64 offset, void *buf,
124                 long *len)
125 {
126         int ret = -1;
127
128         switch (dfu->layout) {
129         case DFU_RAW_ADDR:
130                 ret = nand_block_read(dfu, offset, buf, len);
131                 break;
132         default:
133                 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
134                        dfu_get_layout(dfu->layout));
135         }
136
137         return ret;
138 }
139
140 static int dfu_flush_medium_nand(struct dfu_entity *dfu)
141 {
142         int ret = 0;
143         u64 off;
144
145         /* in case of ubi partition, erase rest of the partition */
146         if (dfu->data.nand.ubi) {
147                 struct mtd_info *mtd = get_nand_dev_by_index(nand_curr_device);
148                 nand_erase_options_t opts;
149
150                 if (nand_curr_device < 0 ||
151                     nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE ||
152                     !mtd) {
153                         printf("%s: invalid nand device\n", __func__);
154                         return -1;
155                 }
156
157                 memset(&opts, 0, sizeof(opts));
158                 off = dfu->offset;
159                 if ((off & (mtd->erasesize - 1)) != 0) {
160                         /*
161                          * last write ended with unaligned length
162                          * sector is erased, jump to next
163                          */
164                         off = off & ~((mtd->erasesize - 1));
165                         off += mtd->erasesize;
166                 }
167                 opts.offset = dfu->data.nand.start + off +
168                                 dfu->bad_skip;
169                 opts.length = dfu->data.nand.start +
170                                 dfu->data.nand.size - opts.offset;
171                 ret = nand_erase_opts(mtd, &opts);
172                 if (ret != 0)
173                         printf("Failure erase: %d\n", ret);
174         }
175
176         return ret;
177 }
178
179 unsigned int dfu_polltimeout_nand(struct dfu_entity *dfu)
180 {
181         /*
182          * Currently, Poll Timeout != 0 is only needed on nand
183          * ubi partition, as the not used sectors need an erase
184          */
185         if (dfu->data.nand.ubi)
186                 return DFU_MANIFEST_POLL_TIMEOUT;
187
188         return DFU_DEFAULT_POLL_TIMEOUT;
189 }
190
191 int dfu_fill_entity_nand(struct dfu_entity *dfu, char *devstr, char *s)
192 {
193         char *st;
194         int ret, dev, part;
195
196         dfu->data.nand.ubi = 0;
197         dfu->dev_type = DFU_DEV_NAND;
198         st = strsep(&s, " ");
199         if (!strcmp(st, "raw")) {
200                 dfu->layout = DFU_RAW_ADDR;
201                 dfu->data.nand.start = simple_strtoul(s, &s, 16);
202                 s++;
203                 dfu->data.nand.size = simple_strtoul(s, &s, 16);
204         } else if ((!strcmp(st, "part")) || (!strcmp(st, "partubi"))) {
205                 char mtd_id[32];
206                 struct mtd_device *mtd_dev;
207                 u8 part_num;
208                 struct part_info *pi;
209
210                 dfu->layout = DFU_RAW_ADDR;
211
212                 dev = simple_strtoul(s, &s, 10);
213                 s++;
214                 part = simple_strtoul(s, &s, 10);
215
216                 sprintf(mtd_id, "%s%d,%d", "nand", dev, part - 1);
217                 printf("using id '%s'\n", mtd_id);
218
219                 mtdparts_init();
220
221                 ret = find_dev_and_part(mtd_id, &mtd_dev, &part_num, &pi);
222                 if (ret != 0) {
223                         printf("Could not locate '%s'\n", mtd_id);
224                         return -1;
225                 }
226
227                 dfu->data.nand.start = pi->offset;
228                 dfu->data.nand.size = pi->size;
229                 if (!strcmp(st, "partubi"))
230                         dfu->data.nand.ubi = 1;
231         } else {
232                 printf("%s: Memory layout (%s) not supported!\n", __func__, st);
233                 return -1;
234         }
235
236         dfu->get_medium_size = dfu_get_medium_size_nand;
237         dfu->read_medium = dfu_read_medium_nand;
238         dfu->write_medium = dfu_write_medium_nand;
239         dfu->flush_medium = dfu_flush_medium_nand;
240         dfu->poll_timeout = dfu_polltimeout_nand;
241
242         /* initial state */
243         dfu->inited = 0;
244
245         return 0;
246 }