Commit c6631764c2a64efc91c84077ca65f4fee153f133
Committed by
Marek Vasut
1 parent
c4df2f4100
Exists in
master
and in
53 other branches
dfu: NAND specific routines for DFU operation
Support for NAND storage devices to work with the DFU framework. Signed-off-by: Pantelis Antoniou <panto@antoniou-consulting.com> Signed-off-by: Tom Rini <trini@ti.com> Acked-by: Scott Wood <scottwood@freescale.com>
Showing 5 changed files with 222 additions and 0 deletions Side-by-side Diff
README
... | ... | @@ -1357,6 +1357,9 @@ |
1357 | 1357 | CONFIG_DFU_MMC |
1358 | 1358 | This enables support for exposing (e)MMC devices via DFU. |
1359 | 1359 | |
1360 | + CONFIG_DFU_NAND | |
1361 | + This enables support for exposing NAND devices via DFU. | |
1362 | + | |
1360 | 1363 | CONFIG_SYS_DFU_MAX_FILE_SIZE |
1361 | 1364 | When updating files rather than the raw storage device, |
1362 | 1365 | we use a static buffer to copy the file into and then write |
drivers/dfu/Makefile
drivers/dfu/dfu.c
... | ... | @@ -85,6 +85,7 @@ |
85 | 85 | /* initial state */ |
86 | 86 | dfu->crc = 0; |
87 | 87 | dfu->offset = 0; |
88 | + dfu->bad_skip = 0; | |
88 | 89 | dfu->i_blk_seq_num = 0; |
89 | 90 | dfu->i_buf_start = dfu_buf; |
90 | 91 | dfu->i_buf_end = dfu_buf + sizeof(dfu_buf); |
... | ... | @@ -233,6 +234,8 @@ |
233 | 234 | dfu->i_buf = dfu->i_buf_start; |
234 | 235 | dfu->b_left = 0; |
235 | 236 | |
237 | + dfu->bad_skip = 0; | |
238 | + | |
236 | 239 | dfu->inited = 1; |
237 | 240 | } |
238 | 241 | |
... | ... | @@ -262,6 +265,8 @@ |
262 | 265 | dfu->i_buf = dfu->i_buf_start; |
263 | 266 | dfu->b_left = 0; |
264 | 267 | |
268 | + dfu->bad_skip = 0; | |
269 | + | |
265 | 270 | dfu->inited = 0; |
266 | 271 | } |
267 | 272 | |
... | ... | @@ -283,6 +288,9 @@ |
283 | 288 | /* Specific for mmc device */ |
284 | 289 | if (strcmp(interface, "mmc") == 0) { |
285 | 290 | if (dfu_fill_entity_mmc(dfu, s)) |
291 | + return -1; | |
292 | + } else if (strcmp(interface, "nand") == 0) { | |
293 | + if (dfu_fill_entity_nand(dfu, s)) | |
286 | 294 | return -1; |
287 | 295 | } else { |
288 | 296 | printf("%s: Device %s not (yet) supported!\n", |
drivers/dfu/dfu_nand.c
1 | +/* | |
2 | + * dfu_nand.c -- DFU for NAND routines. | |
3 | + * | |
4 | + * Copyright (C) 2012-2013 Texas Instruments, Inc. | |
5 | + * | |
6 | + * Based on dfu_mmc.c which is: | |
7 | + * Copyright (C) 2012 Samsung Electronics | |
8 | + * author: Lukasz Majewski <l.majewski@samsung.com> | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify | |
11 | + * it under the terms of the GNU General Public License as published by | |
12 | + * the Free Software Foundation; either version 2 of the License, or | |
13 | + * (at your option) any later version. | |
14 | + * | |
15 | + * This program is distributed in the hope that it will be useful, | |
16 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | + * GNU General Public License for more details. | |
19 | + * | |
20 | + * You should have received a copy of the GNU General Public License | |
21 | + * along with this program; if not, write to the Free Software | |
22 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
23 | + */ | |
24 | + | |
25 | +#include <common.h> | |
26 | +#include <malloc.h> | |
27 | +#include <errno.h> | |
28 | +#include <div64.h> | |
29 | +#include <dfu.h> | |
30 | +#include <linux/mtd/mtd.h> | |
31 | +#include <jffs2/load_kernel.h> | |
32 | +#include <nand.h> | |
33 | + | |
34 | +enum dfu_nand_op { | |
35 | + DFU_OP_READ = 1, | |
36 | + DFU_OP_WRITE, | |
37 | +}; | |
38 | + | |
39 | +static int nand_block_op(enum dfu_nand_op op, struct dfu_entity *dfu, | |
40 | + u64 offset, void *buf, long *len) | |
41 | +{ | |
42 | + loff_t start, lim; | |
43 | + size_t count, actual; | |
44 | + int ret; | |
45 | + nand_info_t *nand; | |
46 | + | |
47 | + /* if buf == NULL return total size of the area */ | |
48 | + if (buf == NULL) { | |
49 | + *len = dfu->data.nand.size; | |
50 | + return 0; | |
51 | + } | |
52 | + | |
53 | + start = dfu->data.nand.start + offset + dfu->bad_skip; | |
54 | + lim = dfu->data.nand.start + dfu->data.nand.size - start; | |
55 | + count = *len; | |
56 | + | |
57 | + if (nand_curr_device < 0 || | |
58 | + nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || | |
59 | + !nand_info[nand_curr_device].name) { | |
60 | + printf("%s: invalid nand device\n", __func__); | |
61 | + return -1; | |
62 | + } | |
63 | + | |
64 | + nand = &nand_info[nand_curr_device]; | |
65 | + | |
66 | + if (op == DFU_OP_READ) | |
67 | + ret = nand_read_skip_bad(nand, start, &count, &actual, | |
68 | + lim, buf); | |
69 | + else | |
70 | + ret = nand_write_skip_bad(nand, start, &count, &actual, | |
71 | + lim, buf, 0); | |
72 | + | |
73 | + if (ret != 0) { | |
74 | + printf("%s: nand_%s_skip_bad call failed at %llx!\n", | |
75 | + __func__, op == DFU_OP_READ ? "read" : "write", | |
76 | + start); | |
77 | + return ret; | |
78 | + } | |
79 | + | |
80 | + /* | |
81 | + * Find out where we stopped writing data. This can be deeper into | |
82 | + * the NAND than we expected due to having to skip bad blocks. So | |
83 | + * we must take this into account for the next write, if any. | |
84 | + */ | |
85 | + if (actual > count) | |
86 | + dfu->bad_skip += actual - count; | |
87 | + | |
88 | + return ret; | |
89 | +} | |
90 | + | |
91 | +static inline int nand_block_write(struct dfu_entity *dfu, | |
92 | + u64 offset, void *buf, long *len) | |
93 | +{ | |
94 | + return nand_block_op(DFU_OP_WRITE, dfu, offset, buf, len); | |
95 | +} | |
96 | + | |
97 | +static inline int nand_block_read(struct dfu_entity *dfu, | |
98 | + u64 offset, void *buf, long *len) | |
99 | +{ | |
100 | + return nand_block_op(DFU_OP_READ, dfu, offset, buf, len); | |
101 | +} | |
102 | + | |
103 | +static int dfu_write_medium_nand(struct dfu_entity *dfu, | |
104 | + u64 offset, void *buf, long *len) | |
105 | +{ | |
106 | + int ret = -1; | |
107 | + | |
108 | + switch (dfu->layout) { | |
109 | + case DFU_RAW_ADDR: | |
110 | + ret = nand_block_write(dfu, offset, buf, len); | |
111 | + break; | |
112 | + default: | |
113 | + printf("%s: Layout (%s) not (yet) supported!\n", __func__, | |
114 | + dfu_get_layout(dfu->layout)); | |
115 | + } | |
116 | + | |
117 | + return ret; | |
118 | +} | |
119 | + | |
120 | +static int dfu_read_medium_nand(struct dfu_entity *dfu, u64 offset, void *buf, | |
121 | + long *len) | |
122 | +{ | |
123 | + int ret = -1; | |
124 | + | |
125 | + switch (dfu->layout) { | |
126 | + case DFU_RAW_ADDR: | |
127 | + ret = nand_block_read(dfu, offset, buf, len); | |
128 | + break; | |
129 | + default: | |
130 | + printf("%s: Layout (%s) not (yet) supported!\n", __func__, | |
131 | + dfu_get_layout(dfu->layout)); | |
132 | + } | |
133 | + | |
134 | + return ret; | |
135 | +} | |
136 | + | |
137 | +int dfu_fill_entity_nand(struct dfu_entity *dfu, char *s) | |
138 | +{ | |
139 | + char *st; | |
140 | + int ret, dev, part; | |
141 | + | |
142 | + dfu->dev_type = DFU_DEV_NAND; | |
143 | + st = strsep(&s, " "); | |
144 | + if (!strcmp(st, "raw")) { | |
145 | + dfu->layout = DFU_RAW_ADDR; | |
146 | + dfu->data.nand.start = simple_strtoul(s, &s, 16); | |
147 | + s++; | |
148 | + dfu->data.nand.size = simple_strtoul(s, &s, 16); | |
149 | + } else if (!strcmp(st, "part")) { | |
150 | + char mtd_id[32]; | |
151 | + struct mtd_device *mtd_dev; | |
152 | + u8 part_num; | |
153 | + struct part_info *pi; | |
154 | + | |
155 | + dfu->layout = DFU_RAW_ADDR; | |
156 | + | |
157 | + dev = simple_strtoul(s, &s, 10); | |
158 | + s++; | |
159 | + part = simple_strtoul(s, &s, 10); | |
160 | + | |
161 | + sprintf(mtd_id, "%s%d,%d", "nand", dev, part - 1); | |
162 | + printf("using id '%s'\n", mtd_id); | |
163 | + | |
164 | + mtdparts_init(); | |
165 | + | |
166 | + ret = find_dev_and_part(mtd_id, &mtd_dev, &part_num, &pi); | |
167 | + if (ret != 0) { | |
168 | + printf("Could not locate '%s'\n", mtd_id); | |
169 | + return -1; | |
170 | + } | |
171 | + | |
172 | + dfu->data.nand.start = pi->offset; | |
173 | + dfu->data.nand.size = pi->size; | |
174 | + | |
175 | + } else { | |
176 | + printf("%s: Memory layout (%s) not supported!\n", __func__, st); | |
177 | + return -1; | |
178 | + } | |
179 | + | |
180 | + dfu->read_medium = dfu_read_medium_nand; | |
181 | + dfu->write_medium = dfu_write_medium_nand; | |
182 | + | |
183 | + /* initial state */ | |
184 | + dfu->inited = 0; | |
185 | + | |
186 | + return 0; | |
187 | +} |
include/dfu.h
... | ... | @@ -52,6 +52,15 @@ |
52 | 52 | unsigned int part; |
53 | 53 | }; |
54 | 54 | |
55 | +struct nand_internal_data { | |
56 | + /* RAW programming */ | |
57 | + u64 start; | |
58 | + u64 size; | |
59 | + | |
60 | + unsigned int dev; | |
61 | + unsigned int part; | |
62 | +}; | |
63 | + | |
55 | 64 | static inline unsigned int get_mmc_blk_size(int dev) |
56 | 65 | { |
57 | 66 | return find_mmc_device(dev)->read_bl_len; |
... | ... | @@ -74,6 +83,7 @@ |
74 | 83 | |
75 | 84 | union { |
76 | 85 | struct mmc_internal_data mmc; |
86 | + struct nand_internal_data nand; | |
77 | 87 | } data; |
78 | 88 | |
79 | 89 | int (*read_medium)(struct dfu_entity *dfu, |
... | ... | @@ -96,6 +106,8 @@ |
96 | 106 | long r_left; |
97 | 107 | long b_left; |
98 | 108 | |
109 | + u32 bad_skip; /* for nand use */ | |
110 | + | |
99 | 111 | unsigned int inited:1; |
100 | 112 | }; |
101 | 113 | |
... | ... | @@ -120,5 +132,16 @@ |
120 | 132 | return -1; |
121 | 133 | } |
122 | 134 | #endif |
135 | + | |
136 | +#ifdef CONFIG_DFU_NAND | |
137 | +extern int dfu_fill_entity_nand(struct dfu_entity *dfu, char *s); | |
138 | +#else | |
139 | +static inline int dfu_fill_entity_nand(struct dfu_entity *dfu, char *s) | |
140 | +{ | |
141 | + puts("NAND support not available!\n"); | |
142 | + return -1; | |
143 | +} | |
144 | +#endif | |
145 | + | |
123 | 146 | #endif /* __DFU_ENTITY_H_ */ |