blob: 72fa03eedaecd3700fea7d4ed4e277fad2112ce5 [file] [log] [blame]
Lukasz Majewski34106b22012-08-06 14:41:08 +02001/*
2 * dfu.c -- DFU back-end routines
3 *
4 * Copyright (C) 2012 Samsung Electronics
5 * author: Lukasz Majewski <l.majewski@samsung.com>
6 *
Wolfgang Denkd79de1d2013-07-08 09:37:19 +02007 * SPDX-License-Identifier: GPL-2.0+
Lukasz Majewski34106b22012-08-06 14:41:08 +02008 */
9
10#include <common.h>
11#include <malloc.h>
Pantelis Antoniou620e9dd2012-11-30 08:01:11 +000012#include <errno.h>
Pantelis Antonioua6e788d2013-03-14 05:32:48 +000013#include <div64.h>
Lukasz Majewski34106b22012-08-06 14:41:08 +020014#include <dfu.h>
Stephen Warren020e6f32014-06-11 12:47:27 -060015#include <ext4fs.h>
16#include <fat.h>
Łukasz Majewskicf6d7db2014-02-21 08:23:00 +010017#include <mmc.h>
Lukasz Majewski34106b22012-08-06 14:41:08 +020018
Pantelis Antonioua6e788d2013-03-14 05:32:48 +000019static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE)
20 dfu_file_buf[CONFIG_SYS_DFU_MAX_FILE_SIZE];
21static long dfu_file_buf_len;
22
Lukasz Majewski4da65ea2014-05-09 16:58:15 +020023static int mmc_access_part(struct dfu_entity *dfu, struct mmc *mmc, int part)
24{
25 int ret;
26
27 if (part == mmc->part_num)
28 return 0;
29
Stephen Warren4afe50f2014-06-11 16:03:33 -060030 ret = mmc_switch_part(dfu->data.mmc.dev_num, part);
Lukasz Majewski4da65ea2014-05-09 16:58:15 +020031 if (ret) {
32 error("Cannot switch to partition %d\n", part);
33 return ret;
34 }
35 mmc->part_num = part;
36
37 return 0;
38}
39
Afzal Mohammedb9a4a6b2013-09-18 01:14:50 +053040static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu,
Pantelis Antonioua6e788d2013-03-14 05:32:48 +000041 u64 offset, void *buf, long *len)
Lukasz Majewski34106b22012-08-06 14:41:08 +020042{
Stephen Warren4afe50f2014-06-11 16:03:33 -060043 struct mmc *mmc = find_mmc_device(dfu->data.mmc.dev_num);
Łukasz Majewskicf6d7db2014-02-21 08:23:00 +010044 u32 blk_start, blk_count, n = 0;
Lukasz Majewski4da65ea2014-05-09 16:58:15 +020045 int ret, part_num_bkp = 0;
Lukasz Majewski34106b22012-08-06 14:41:08 +020046
Pantelis Antonioua6e788d2013-03-14 05:32:48 +000047 /*
48 * We must ensure that we work in lba_blk_size chunks, so ALIGN
49 * this value.
50 */
51 *len = ALIGN(*len, dfu->data.mmc.lba_blk_size);
Lukasz Majewski34106b22012-08-06 14:41:08 +020052
Pantelis Antonioua6e788d2013-03-14 05:32:48 +000053 blk_start = dfu->data.mmc.lba_start +
54 (u32)lldiv(offset, dfu->data.mmc.lba_blk_size);
55 blk_count = *len / dfu->data.mmc.lba_blk_size;
56 if (blk_start + blk_count >
57 dfu->data.mmc.lba_start + dfu->data.mmc.lba_size) {
58 puts("Request would exceed designated area!\n");
59 return -EINVAL;
60 }
61
Lukasz Majewski4da65ea2014-05-09 16:58:15 +020062 if (dfu->data.mmc.hw_partition >= 0) {
63 part_num_bkp = mmc->part_num;
64 ret = mmc_access_part(dfu, mmc, dfu->data.mmc.hw_partition);
65 if (ret)
66 return ret;
67 }
68
Łukasz Majewskicf6d7db2014-02-21 08:23:00 +010069 debug("%s: %s dev: %d start: %d cnt: %d buf: 0x%p\n", __func__,
Stephen Warren4afe50f2014-06-11 16:03:33 -060070 op == DFU_OP_READ ? "MMC READ" : "MMC WRITE",
71 dfu->data.mmc.dev_num, blk_start, blk_count, buf);
Łukasz Majewskicf6d7db2014-02-21 08:23:00 +010072 switch (op) {
73 case DFU_OP_READ:
Stephen Warren4afe50f2014-06-11 16:03:33 -060074 n = mmc->block_dev.block_read(dfu->data.mmc.dev_num, blk_start,
Łukasz Majewskicf6d7db2014-02-21 08:23:00 +010075 blk_count, buf);
76 break;
77 case DFU_OP_WRITE:
Stephen Warren4afe50f2014-06-11 16:03:33 -060078 n = mmc->block_dev.block_write(dfu->data.mmc.dev_num, blk_start,
Łukasz Majewskicf6d7db2014-02-21 08:23:00 +010079 blk_count, buf);
80 break;
81 default:
82 error("Operation not supported\n");
83 }
84
85 if (n != blk_count) {
86 error("MMC operation failed");
Lukasz Majewski4da65ea2014-05-09 16:58:15 +020087 if (dfu->data.mmc.hw_partition >= 0)
88 mmc_access_part(dfu, mmc, part_num_bkp);
Łukasz Majewskicf6d7db2014-02-21 08:23:00 +010089 return -EIO;
90 }
Lukasz Majewski34106b22012-08-06 14:41:08 +020091
Lukasz Majewski4da65ea2014-05-09 16:58:15 +020092 if (dfu->data.mmc.hw_partition >= 0) {
93 ret = mmc_access_part(dfu, mmc, part_num_bkp);
94 if (ret)
95 return ret;
96 }
97
Łukasz Majewskicf6d7db2014-02-21 08:23:00 +010098 return 0;
Lukasz Majewski34106b22012-08-06 14:41:08 +020099}
100
Pantelis Antonioua6e788d2013-03-14 05:32:48 +0000101static int mmc_file_buffer(struct dfu_entity *dfu, void *buf, long *len)
Lukasz Majewski34106b22012-08-06 14:41:08 +0200102{
Pantelis Antonioua6e788d2013-03-14 05:32:48 +0000103 if (dfu_file_buf_len + *len > CONFIG_SYS_DFU_MAX_FILE_SIZE) {
104 dfu_file_buf_len = 0;
105 return -EINVAL;
106 }
Lukasz Majewski34106b22012-08-06 14:41:08 +0200107
Pantelis Antonioua6e788d2013-03-14 05:32:48 +0000108 /* Add to the current buffer. */
109 memcpy(dfu_file_buf + dfu_file_buf_len, buf, *len);
110 dfu_file_buf_len += *len;
111
112 return 0;
Lukasz Majewski34106b22012-08-06 14:41:08 +0200113}
114
Afzal Mohammedb9a4a6b2013-09-18 01:14:50 +0530115static int mmc_file_op(enum dfu_op op, struct dfu_entity *dfu,
Lukasz Majewski34106b22012-08-06 14:41:08 +0200116 void *buf, long *len)
117{
Stephen Warren020e6f32014-06-11 12:47:27 -0600118 const char *fsname, *opname;
Lukasz Majewski34106b22012-08-06 14:41:08 +0200119 char cmd_buf[DFU_CMD_BUF_SIZE];
120 char *str_env;
121 int ret;
122
Łukasz Majewski71c88eb2012-08-23 23:33:55 +0000123 switch (dfu->layout) {
124 case DFU_FS_FAT:
Stephen Warren020e6f32014-06-11 12:47:27 -0600125 fsname = "fat";
Łukasz Majewski71c88eb2012-08-23 23:33:55 +0000126 break;
127 case DFU_FS_EXT4:
Stephen Warren020e6f32014-06-11 12:47:27 -0600128 fsname = "ext4";
Łukasz Majewski71c88eb2012-08-23 23:33:55 +0000129 break;
130 default:
131 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
132 dfu_get_layout(dfu->layout));
Pantelis Antonioua6e788d2013-03-14 05:32:48 +0000133 return -1;
Łukasz Majewski71c88eb2012-08-23 23:33:55 +0000134 }
Lukasz Majewski34106b22012-08-06 14:41:08 +0200135
Stephen Warren020e6f32014-06-11 12:47:27 -0600136 switch (op) {
137 case DFU_OP_READ:
138 opname = "load";
139 break;
140 case DFU_OP_WRITE:
141 opname = "write";
142 break;
143 case DFU_OP_SIZE:
144 opname = "size";
145 break;
146 default:
147 return -1;
148 }
149
150 sprintf(cmd_buf, "%s%s mmc %d:%d", fsname, opname,
151 dfu->data.mmc.dev, dfu->data.mmc.part);
152
153 if (op != DFU_OP_SIZE)
154 sprintf(cmd_buf + strlen(cmd_buf), " 0x%x", (unsigned int)buf);
155
156 sprintf(cmd_buf + strlen(cmd_buf), " %s", dfu->name);
157
Lukasz Majewski8f957c02014-02-20 10:29:18 +0100158 if (op == DFU_OP_WRITE)
159 sprintf(cmd_buf + strlen(cmd_buf), " %lx", *len);
160
Lukasz Majewski34106b22012-08-06 14:41:08 +0200161 debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
162
163 ret = run_command(cmd_buf, 0);
164 if (ret) {
165 puts("dfu: Read error!\n");
166 return ret;
167 }
168
Stephen Warren020e6f32014-06-11 12:47:27 -0600169 if (op != DFU_OP_WRITE) {
Lukasz Majewski34106b22012-08-06 14:41:08 +0200170 str_env = getenv("filesize");
171 if (str_env == NULL) {
172 puts("dfu: Wrong file size!\n");
173 return -1;
174 }
175 *len = simple_strtoul(str_env, NULL, 16);
176 }
177
178 return ret;
179}
180
Pantelis Antonioua6e788d2013-03-14 05:32:48 +0000181int dfu_write_medium_mmc(struct dfu_entity *dfu,
182 u64 offset, void *buf, long *len)
Lukasz Majewski34106b22012-08-06 14:41:08 +0200183{
184 int ret = -1;
185
186 switch (dfu->layout) {
187 case DFU_RAW_ADDR:
Pantelis Antonioua6e788d2013-03-14 05:32:48 +0000188 ret = mmc_block_op(DFU_OP_WRITE, dfu, offset, buf, len);
Lukasz Majewski34106b22012-08-06 14:41:08 +0200189 break;
190 case DFU_FS_FAT:
Łukasz Majewski71c88eb2012-08-23 23:33:55 +0000191 case DFU_FS_EXT4:
Pantelis Antonioua6e788d2013-03-14 05:32:48 +0000192 ret = mmc_file_buffer(dfu, buf, len);
Lukasz Majewski34106b22012-08-06 14:41:08 +0200193 break;
194 default:
195 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
196 dfu_get_layout(dfu->layout));
197 }
198
199 return ret;
200}
201
Pantelis Antonioua6e788d2013-03-14 05:32:48 +0000202int dfu_flush_medium_mmc(struct dfu_entity *dfu)
203{
204 int ret = 0;
205
206 if (dfu->layout != DFU_RAW_ADDR) {
207 /* Do stuff here. */
208 ret = mmc_file_op(DFU_OP_WRITE, dfu, &dfu_file_buf,
209 &dfu_file_buf_len);
210
211 /* Now that we're done */
212 dfu_file_buf_len = 0;
213 }
214
215 return ret;
216}
217
Stephen Warren020e6f32014-06-11 12:47:27 -0600218long dfu_get_medium_size_mmc(struct dfu_entity *dfu)
219{
220 int ret;
221 long len;
222
223 switch (dfu->layout) {
224 case DFU_RAW_ADDR:
225 return dfu->data.mmc.lba_size * dfu->data.mmc.lba_blk_size;
226 case DFU_FS_FAT:
227 case DFU_FS_EXT4:
228 ret = mmc_file_op(DFU_OP_SIZE, dfu, NULL, &len);
229 if (ret < 0)
230 return ret;
231 return len;
232 default:
233 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
234 dfu_get_layout(dfu->layout));
235 return -1;
236 }
237}
238
Pantelis Antonioua6e788d2013-03-14 05:32:48 +0000239int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf,
240 long *len)
Lukasz Majewski34106b22012-08-06 14:41:08 +0200241{
242 int ret = -1;
243
244 switch (dfu->layout) {
245 case DFU_RAW_ADDR:
Pantelis Antonioua6e788d2013-03-14 05:32:48 +0000246 ret = mmc_block_op(DFU_OP_READ, dfu, offset, buf, len);
Lukasz Majewski34106b22012-08-06 14:41:08 +0200247 break;
248 case DFU_FS_FAT:
Łukasz Majewski71c88eb2012-08-23 23:33:55 +0000249 case DFU_FS_EXT4:
Pantelis Antonioua6e788d2013-03-14 05:32:48 +0000250 ret = mmc_file_op(DFU_OP_READ, dfu, buf, len);
Lukasz Majewski34106b22012-08-06 14:41:08 +0200251 break;
252 default:
253 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
254 dfu_get_layout(dfu->layout));
255 }
256
257 return ret;
258}
259
Mateusz Zalegadcb6ea62014-04-28 21:13:24 +0200260/*
261 * @param s Parameter string containing space-separated arguments:
262 * 1st:
263 * raw (raw read/write)
264 * fat (files)
265 * ext4 (^)
266 * part (partition image)
267 * 2nd and 3rd:
268 * lba_start and lba_size, for raw write
269 * mmc_dev and mmc_part, for filesystems and part
Lukasz Majewski4da65ea2014-05-09 16:58:15 +0200270 * 4th (optional):
271 * mmcpart <num> (access to HW eMMC partitions)
Mateusz Zalegadcb6ea62014-04-28 21:13:24 +0200272 */
Stephen Warren4afe50f2014-06-11 16:03:33 -0600273int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *devstr, char *s)
Lukasz Majewski34106b22012-08-06 14:41:08 +0200274{
Mateusz Zalegadcb6ea62014-04-28 21:13:24 +0200275 const char *entity_type;
276 size_t second_arg;
277 size_t third_arg;
Pantelis Antoniou620e9dd2012-11-30 08:01:11 +0000278
Mateusz Zalegadcb6ea62014-04-28 21:13:24 +0200279 struct mmc *mmc;
Pantelis Antoniou620e9dd2012-11-30 08:01:11 +0000280
Mateusz Zalegadcb6ea62014-04-28 21:13:24 +0200281 const char *argv[3];
282 const char **parg = argv;
Pantelis Antoniou620e9dd2012-11-30 08:01:11 +0000283
Stephen Warren4afe50f2014-06-11 16:03:33 -0600284 dfu->data.mmc.dev_num = simple_strtoul(devstr, NULL, 10);
285
Mateusz Zalegadcb6ea62014-04-28 21:13:24 +0200286 for (; parg < argv + sizeof(argv) / sizeof(*argv); ++parg) {
287 *parg = strsep(&s, " ");
288 if (*parg == NULL) {
289 error("Invalid number of arguments.\n");
Pantelis Antoniou620e9dd2012-11-30 08:01:11 +0000290 return -ENODEV;
291 }
Mateusz Zalegadcb6ea62014-04-28 21:13:24 +0200292 }
293
294 entity_type = argv[0];
Mateusz Zalega0ab80bf2014-04-28 21:13:25 +0200295 /*
296 * Base 0 means we'll accept (prefixed with 0x or 0) base 16, 8,
297 * with default 10.
298 */
299 second_arg = simple_strtoul(argv[1], NULL, 0);
300 third_arg = simple_strtoul(argv[2], NULL, 0);
Mateusz Zalegadcb6ea62014-04-28 21:13:24 +0200301
Stephen Warren4afe50f2014-06-11 16:03:33 -0600302 mmc = find_mmc_device(dfu->data.mmc.dev_num);
Mateusz Zalegadcb6ea62014-04-28 21:13:24 +0200303 if (mmc == NULL) {
Stephen Warren4afe50f2014-06-11 16:03:33 -0600304 error("Couldn't find MMC device no. %d.\n",
305 dfu->data.mmc.dev_num);
Mateusz Zalegadcb6ea62014-04-28 21:13:24 +0200306 return -ENODEV;
307 }
308
309 if (mmc_init(mmc)) {
310 error("Couldn't init MMC device.\n");
311 return -ENODEV;
312 }
313
Lukasz Majewski4da65ea2014-05-09 16:58:15 +0200314 dfu->data.mmc.hw_partition = -EINVAL;
Mateusz Zalegadcb6ea62014-04-28 21:13:24 +0200315 if (!strcmp(entity_type, "raw")) {
316 dfu->layout = DFU_RAW_ADDR;
317 dfu->data.mmc.lba_start = second_arg;
318 dfu->data.mmc.lba_size = third_arg;
319 dfu->data.mmc.lba_blk_size = mmc->read_bl_len;
Lukasz Majewski4da65ea2014-05-09 16:58:15 +0200320
321 /*
322 * Check for an extra entry at dfu_alt_info env variable
323 * specifying the mmc HW defined partition number
324 */
325 if (s)
326 if (!strcmp(strsep(&s, " "), "mmcpart"))
327 dfu->data.mmc.hw_partition =
328 simple_strtoul(s, NULL, 0);
329
Mateusz Zalegadcb6ea62014-04-28 21:13:24 +0200330 } else if (!strcmp(entity_type, "part")) {
331 disk_partition_t partinfo;
332 block_dev_desc_t *blk_dev = &mmc->block_dev;
333 int mmcdev = second_arg;
334 int mmcpart = third_arg;
Pantelis Antoniou620e9dd2012-11-30 08:01:11 +0000335
Mateusz Zalegadcb6ea62014-04-28 21:13:24 +0200336 if (get_partition_info(blk_dev, mmcpart, &partinfo) != 0) {
337 error("Couldn't find part #%d on mmc device #%d\n",
338 mmcpart, mmcdev);
Pantelis Antoniou620e9dd2012-11-30 08:01:11 +0000339 return -ENODEV;
340 }
341
Mateusz Zalegadcb6ea62014-04-28 21:13:24 +0200342 dfu->layout = DFU_RAW_ADDR;
343 dfu->data.mmc.lba_start = partinfo.start;
344 dfu->data.mmc.lba_size = partinfo.size;
345 dfu->data.mmc.lba_blk_size = partinfo.blksz;
346 } else if (!strcmp(entity_type, "fat")) {
347 dfu->layout = DFU_FS_FAT;
348 } else if (!strcmp(entity_type, "ext4")) {
349 dfu->layout = DFU_FS_EXT4;
Lukasz Majewski34106b22012-08-06 14:41:08 +0200350 } else {
Mateusz Zalegadcb6ea62014-04-28 21:13:24 +0200351 error("Memory layout (%s) not supported!\n", entity_type);
Pantelis Antoniou620e9dd2012-11-30 08:01:11 +0000352 return -ENODEV;
Lukasz Majewski34106b22012-08-06 14:41:08 +0200353 }
354
Mateusz Zalegadcb6ea62014-04-28 21:13:24 +0200355 /* if it's NOT a raw write */
356 if (strcmp(entity_type, "raw")) {
357 dfu->data.mmc.dev = second_arg;
358 dfu->data.mmc.part = third_arg;
Łukasz Majewski71c88eb2012-08-23 23:33:55 +0000359 }
360
Mateusz Zalegadcb6ea62014-04-28 21:13:24 +0200361 dfu->dev_type = DFU_DEV_MMC;
Stephen Warren020e6f32014-06-11 12:47:27 -0600362 dfu->get_medium_size = dfu_get_medium_size_mmc;
Lukasz Majewski34106b22012-08-06 14:41:08 +0200363 dfu->read_medium = dfu_read_medium_mmc;
364 dfu->write_medium = dfu_write_medium_mmc;
Pantelis Antonioua6e788d2013-03-14 05:32:48 +0000365 dfu->flush_medium = dfu_flush_medium_mmc;
Pantelis Antonioua6e788d2013-03-14 05:32:48 +0000366 dfu->inited = 0;
Lukasz Majewski34106b22012-08-06 14:41:08 +0200367
368 return 0;
369}