]>
Commit | Line | Data |
---|---|---|
bf8940d3 MR |
1 | /* |
2 | * Copyright 2014 Broadcom Corporation. | |
3 | * Copyright 2015 Free Electrons. | |
4 | * | |
5 | * SPDX-License-Identifier: GPL-2.0+ | |
6 | */ | |
7 | ||
8 | #include <config.h> | |
9 | #include <common.h> | |
10 | ||
bf8940d3 | 11 | #include <fastboot.h> |
3d4ef38d | 12 | #include <image-sparse.h> |
bf8940d3 MR |
13 | #include <sparse_format.h> |
14 | ||
15 | #include <linux/mtd/mtd.h> | |
16 | #include <jffs2/jffs2.h> | |
17 | #include <nand.h> | |
18 | ||
19 | static char *response_str; | |
20 | ||
21 | struct fb_nand_sparse { | |
22 | nand_info_t *nand; | |
23 | struct part_info *part; | |
24 | }; | |
25 | ||
6fb77c48 MR |
26 | __weak int board_fastboot_erase_partition_setup(char *name) |
27 | { | |
28 | return 0; | |
29 | } | |
30 | ||
31 | __weak int board_fastboot_write_partition_setup(char *name) | |
32 | { | |
33 | return 0; | |
34 | } | |
35 | ||
bf8940d3 MR |
36 | static int fb_nand_lookup(const char *partname, char *response, |
37 | nand_info_t **nand, | |
38 | struct part_info **part) | |
39 | { | |
40 | struct mtd_device *dev; | |
41 | int ret; | |
42 | u8 pnum; | |
43 | ||
44 | ret = mtdparts_init(); | |
45 | if (ret) { | |
46 | error("Cannot initialize MTD partitions\n"); | |
47 | fastboot_fail(response_str, "cannot init mtdparts"); | |
48 | return ret; | |
49 | } | |
50 | ||
51 | ret = find_dev_and_part(partname, &dev, &pnum, part); | |
52 | if (ret) { | |
53 | error("cannot find partition: '%s'", partname); | |
54 | fastboot_fail(response_str, "cannot find partition"); | |
55 | return ret; | |
56 | } | |
57 | ||
58 | if (dev->id->type != MTD_DEV_TYPE_NAND) { | |
59 | error("partition '%s' is not stored on a NAND device", | |
60 | partname); | |
61 | fastboot_fail(response_str, "not a NAND device"); | |
62 | return -EINVAL; | |
63 | } | |
64 | ||
65 | *nand = &nand_info[dev->id->num]; | |
66 | ||
67 | return 0; | |
68 | } | |
69 | ||
70 | static int _fb_nand_erase(nand_info_t *nand, struct part_info *part) | |
71 | { | |
72 | nand_erase_options_t opts; | |
73 | int ret; | |
74 | ||
75 | memset(&opts, 0, sizeof(opts)); | |
76 | opts.offset = part->offset; | |
77 | opts.length = part->size; | |
78 | opts.quiet = 1; | |
79 | ||
80 | printf("Erasing blocks 0x%llx to 0x%llx\n", | |
81 | part->offset, part->offset + part->size); | |
82 | ||
83 | ret = nand_erase_opts(nand, &opts); | |
84 | if (ret) | |
85 | return ret; | |
86 | ||
87 | printf("........ erased 0x%llx bytes from '%s'\n", | |
88 | part->size, part->name); | |
89 | ||
90 | return 0; | |
91 | } | |
92 | ||
93 | static int _fb_nand_write(nand_info_t *nand, struct part_info *part, | |
94 | void *buffer, unsigned int offset, | |
95 | unsigned int length, size_t *written) | |
96 | { | |
97 | int flags = WITH_WR_VERIFY; | |
98 | ||
99 | #ifdef CONFIG_FASTBOOT_FLASH_NAND_TRIMFFS | |
100 | flags |= WITH_DROP_FFS; | |
101 | #endif | |
102 | ||
103 | return nand_write_skip_bad(nand, offset, &length, written, | |
104 | part->size - (offset - part->offset), | |
105 | buffer, flags); | |
106 | } | |
107 | ||
108 | static int fb_nand_sparse_write(struct sparse_storage *storage, | |
109 | void *priv, | |
110 | unsigned int offset, | |
111 | unsigned int size, | |
112 | char *data) | |
113 | { | |
114 | struct fb_nand_sparse *sparse = priv; | |
115 | size_t written; | |
116 | int ret; | |
117 | ||
118 | ret = _fb_nand_write(sparse->nand, sparse->part, data, | |
119 | offset * storage->block_sz, | |
120 | size * storage->block_sz, &written); | |
121 | if (ret < 0) { | |
122 | printf("Failed to write sparse chunk\n"); | |
123 | return ret; | |
124 | } | |
125 | ||
126 | return written / storage->block_sz; | |
127 | } | |
128 | ||
129 | void fb_nand_flash_write(const char *partname, unsigned int session_id, | |
130 | void *download_buffer, unsigned int download_bytes, | |
131 | char *response) | |
132 | { | |
133 | struct part_info *part; | |
134 | nand_info_t *nand = NULL; | |
135 | int ret; | |
136 | ||
137 | /* initialize the response buffer */ | |
138 | response_str = response; | |
139 | ||
140 | ret = fb_nand_lookup(partname, response, &nand, &part); | |
141 | if (ret) { | |
142 | error("invalid NAND device"); | |
143 | fastboot_fail(response_str, "invalid NAND device"); | |
144 | return; | |
145 | } | |
146 | ||
6fb77c48 MR |
147 | ret = board_fastboot_write_partition_setup(part->name); |
148 | if (ret) | |
149 | return; | |
150 | ||
bf8940d3 MR |
151 | if (is_sparse_image(download_buffer)) { |
152 | struct fb_nand_sparse sparse_priv; | |
153 | sparse_storage_t sparse; | |
154 | ||
155 | sparse_priv.nand = nand; | |
156 | sparse_priv.part = part; | |
157 | ||
158 | sparse.block_sz = nand->writesize; | |
159 | sparse.start = part->offset / sparse.block_sz; | |
160 | sparse.size = part->size / sparse.block_sz; | |
161 | sparse.name = part->name; | |
162 | sparse.write = fb_nand_sparse_write; | |
163 | ||
164 | ret = store_sparse_image(&sparse, &sparse_priv, session_id, | |
165 | download_buffer); | |
166 | } else { | |
167 | printf("Flashing raw image at offset 0x%llx\n", | |
168 | part->offset); | |
169 | ||
170 | ret = _fb_nand_write(nand, part, download_buffer, part->offset, | |
171 | download_bytes, NULL); | |
172 | ||
173 | printf("........ wrote %u bytes to '%s'\n", | |
174 | download_bytes, part->name); | |
175 | } | |
176 | ||
177 | if (ret) { | |
178 | fastboot_fail(response_str, "error writing the image"); | |
179 | return; | |
180 | } | |
181 | ||
182 | fastboot_okay(response_str, ""); | |
183 | } | |
184 | ||
185 | void fb_nand_erase(const char *partname, char *response) | |
186 | { | |
187 | struct part_info *part; | |
188 | nand_info_t *nand = NULL; | |
189 | int ret; | |
190 | ||
191 | /* initialize the response buffer */ | |
192 | response_str = response; | |
193 | ||
194 | ret = fb_nand_lookup(partname, response, &nand, &part); | |
195 | if (ret) { | |
196 | error("invalid NAND device"); | |
197 | fastboot_fail(response_str, "invalid NAND device"); | |
198 | return; | |
199 | } | |
200 | ||
6fb77c48 MR |
201 | ret = board_fastboot_erase_partition_setup(part->name); |
202 | if (ret) | |
203 | return; | |
204 | ||
bf8940d3 MR |
205 | ret = _fb_nand_erase(nand, part); |
206 | if (ret) { | |
207 | error("failed erasing from device %s", nand->name); | |
208 | fastboot_fail(response_str, "failed erasing from device"); | |
209 | return; | |
210 | } | |
211 | ||
212 | fastboot_okay(response_str, ""); | |
213 | } |