]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
c6631764 PA |
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 <[email protected]> | |
c6631764 PA |
10 | */ |
11 | ||
f7ae49fc | 12 | #include <log.h> |
c6631764 PA |
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 | ||
5a127c84 | 21 | static int nand_block_op(enum dfu_op op, struct dfu_entity *dfu, |
c6631764 PA |
22 | u64 offset, void *buf, long *len) |
23 | { | |
24 | loff_t start, lim; | |
25 | size_t count, actual; | |
26 | int ret; | |
151c06ec | 27 | struct mtd_info *mtd; |
c6631764 PA |
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 | ||
16520189 GS |
39 | mtd = get_nand_dev_by_index(nand_curr_device); |
40 | ||
c6631764 PA |
41 | if (nand_curr_device < 0 || |
42 | nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || | |
16520189 | 43 | !mtd) { |
c6631764 PA |
44 | printf("%s: invalid nand device\n", __func__); |
45 | return -1; | |
46 | } | |
47 | ||
a67cc37e | 48 | if (op == DFU_OP_READ) { |
151c06ec SW |
49 | ret = nand_read_skip_bad(mtd, start, &count, &actual, |
50 | lim, buf); | |
a67cc37e HS |
51 | } else { |
52 | nand_erase_options_t opts; | |
13cb7cc9 | 53 | int write_flags = WITH_WR_VERIFY; |
a67cc37e HS |
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 */ | |
151c06ec | 62 | ret = nand_erase_opts(mtd, &opts); |
a67cc37e HS |
63 | if (ret) |
64 | return ret; | |
65 | /* then write */ | |
13cb7cc9 GR |
66 | #ifdef CONFIG_DFU_NAND_TRIMFFS |
67 | if (dfu->data.nand.ubi) | |
68 | write_flags |= WITH_DROP_FFS; | |
69 | #endif | |
151c06ec | 70 | ret = nand_write_skip_bad(mtd, start, &count, &actual, |
13cb7cc9 | 71 | lim, buf, write_flags); |
a67cc37e | 72 | } |
c6631764 PA |
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 | ||
15970d87 | 121 | int dfu_get_medium_size_nand(struct dfu_entity *dfu, u64 *size) |
0e285b50 | 122 | { |
4de51201 PD |
123 | *size = dfu->data.nand.size; |
124 | ||
125 | return 0; | |
0e285b50 SW |
126 | } |
127 | ||
c6631764 PA |
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 | ||
815c30b2 HS |
145 | static int dfu_flush_medium_nand(struct dfu_entity *dfu) |
146 | { | |
147 | int ret = 0; | |
9ae63f46 | 148 | u64 off; |
815c30b2 HS |
149 | |
150 | /* in case of ubi partition, erase rest of the partition */ | |
151 | if (dfu->data.nand.ubi) { | |
16520189 | 152 | struct mtd_info *mtd = get_nand_dev_by_index(nand_curr_device); |
815c30b2 HS |
153 | nand_erase_options_t opts; |
154 | ||
155 | if (nand_curr_device < 0 || | |
156 | nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || | |
16520189 | 157 | !mtd) { |
815c30b2 HS |
158 | printf("%s: invalid nand device\n", __func__); |
159 | return -1; | |
160 | } | |
161 | ||
815c30b2 | 162 | memset(&opts, 0, sizeof(opts)); |
9ae63f46 HS |
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 + | |
815c30b2 HS |
173 | dfu->bad_skip; |
174 | opts.length = dfu->data.nand.start + | |
175 | dfu->data.nand.size - opts.offset; | |
151c06ec | 176 | ret = nand_erase_opts(mtd, &opts); |
815c30b2 HS |
177 | if (ret != 0) |
178 | printf("Failure erase: %d\n", ret); | |
179 | } | |
180 | ||
181 | return ret; | |
182 | } | |
183 | ||
fc25fa27 HS |
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 | ||
53b40636 | 196 | int dfu_fill_entity_nand(struct dfu_entity *dfu, char *devstr, char **argv, int argc) |
c6631764 | 197 | { |
53b40636 | 198 | char *s; |
c6631764 PA |
199 | int ret, dev, part; |
200 | ||
815c30b2 | 201 | dfu->data.nand.ubi = 0; |
c6631764 | 202 | dfu->dev_type = DFU_DEV_NAND; |
53b40636 MH |
203 | if (argc != 3) |
204 | return -EINVAL; | |
205 | ||
206 | if (!strcmp(argv[0], "raw")) { | |
c6631764 | 207 | dfu->layout = DFU_RAW_ADDR; |
53b40636 MH |
208 | dfu->data.nand.start = hextoul(argv[1], &s); |
209 | if (*s) | |
210 | return -EINVAL; | |
211 | dfu->data.nand.size = hextoul(argv[2], &s); | |
212 | if (*s) | |
213 | return -EINVAL; | |
214 | } else if ((!strcmp(argv[0], "part")) || (!strcmp(argv[0], "partubi"))) { | |
c6631764 PA |
215 | char mtd_id[32]; |
216 | struct mtd_device *mtd_dev; | |
217 | u8 part_num; | |
218 | struct part_info *pi; | |
219 | ||
220 | dfu->layout = DFU_RAW_ADDR; | |
221 | ||
53b40636 MH |
222 | dev = dectoul(argv[1], &s); |
223 | if (*s) | |
224 | return -EINVAL; | |
225 | part = dectoul(argv[2], &s); | |
226 | if (*s) | |
227 | return -EINVAL; | |
c6631764 PA |
228 | |
229 | sprintf(mtd_id, "%s%d,%d", "nand", dev, part - 1); | |
0a815ff7 | 230 | debug("using id '%s'\n", mtd_id); |
c6631764 PA |
231 | |
232 | mtdparts_init(); | |
233 | ||
234 | ret = find_dev_and_part(mtd_id, &mtd_dev, &part_num, &pi); | |
235 | if (ret != 0) { | |
236 | printf("Could not locate '%s'\n", mtd_id); | |
237 | return -1; | |
238 | } | |
239 | ||
240 | dfu->data.nand.start = pi->offset; | |
241 | dfu->data.nand.size = pi->size; | |
53b40636 | 242 | if (!strcmp(argv[0], "partubi")) |
815c30b2 | 243 | dfu->data.nand.ubi = 1; |
c6631764 | 244 | } else { |
53b40636 | 245 | printf("%s: Memory layout (%s) not supported!\n", __func__, argv[0]); |
c6631764 PA |
246 | return -1; |
247 | } | |
248 | ||
0e285b50 | 249 | dfu->get_medium_size = dfu_get_medium_size_nand; |
c6631764 PA |
250 | dfu->read_medium = dfu_read_medium_nand; |
251 | dfu->write_medium = dfu_write_medium_nand; | |
815c30b2 | 252 | dfu->flush_medium = dfu_flush_medium_nand; |
fc25fa27 | 253 | dfu->poll_timeout = dfu_polltimeout_nand; |
c6631764 PA |
254 | |
255 | /* initial state */ | |
256 | dfu->inited = 0; | |
257 | ||
258 | return 0; | |
259 | } |