]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - drivers/dfu/dfu_mmc.c
Merge branch 'master' of git://git.denx.de/u-boot-tegra
[karo-tx-uboot.git] / drivers / dfu / dfu_mmc.c
1 /*
2  * dfu.c -- DFU back-end routines
3  *
4  * Copyright (C) 2012 Samsung Electronics
5  * author: Lukasz Majewski <l.majewski@samsung.com>
6  *
7  * SPDX-License-Identifier:     GPL-2.0+
8  */
9
10 #include <common.h>
11 #include <malloc.h>
12 #include <errno.h>
13 #include <div64.h>
14 #include <dfu.h>
15 #include <ext4fs.h>
16 #include <fat.h>
17 #include <mmc.h>
18
19 static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE)
20                                 dfu_file_buf[CONFIG_SYS_DFU_MAX_FILE_SIZE];
21 static long dfu_file_buf_len;
22
23 static 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
30         ret = mmc_switch_part(dfu->data.mmc.dev_num, part);
31         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
40 static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu,
41                         u64 offset, void *buf, long *len)
42 {
43         struct mmc *mmc = find_mmc_device(dfu->data.mmc.dev_num);
44         u32 blk_start, blk_count, n = 0;
45         int ret, part_num_bkp = 0;
46
47         /*
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);
52
53         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
62         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
69         debug("%s: %s dev: %d start: %d cnt: %d buf: 0x%p\n", __func__,
70               op == DFU_OP_READ ? "MMC READ" : "MMC WRITE",
71               dfu->data.mmc.dev_num, blk_start, blk_count, buf);
72         switch (op) {
73         case DFU_OP_READ:
74                 n = mmc->block_dev.block_read(dfu->data.mmc.dev_num, blk_start,
75                                               blk_count, buf);
76                 break;
77         case DFU_OP_WRITE:
78                 n = mmc->block_dev.block_write(dfu->data.mmc.dev_num, blk_start,
79                                                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");
87                 if (dfu->data.mmc.hw_partition >= 0)
88                         mmc_access_part(dfu, mmc, part_num_bkp);
89                 return -EIO;
90         }
91
92         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
98         return 0;
99 }
100
101 static int mmc_file_buffer(struct dfu_entity *dfu, void *buf, long *len)
102 {
103         if (dfu_file_buf_len + *len > CONFIG_SYS_DFU_MAX_FILE_SIZE) {
104                 dfu_file_buf_len = 0;
105                 return -EINVAL;
106         }
107
108         /* 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;
113 }
114
115 static int mmc_file_op(enum dfu_op op, struct dfu_entity *dfu,
116                         void *buf, long *len)
117 {
118         const char *fsname, *opname;
119         char cmd_buf[DFU_CMD_BUF_SIZE];
120         char *str_env;
121         int ret;
122
123         switch (dfu->layout) {
124         case DFU_FS_FAT:
125                 fsname = "fat";
126                 break;
127         case DFU_FS_EXT4:
128                 fsname = "ext4";
129                 break;
130         default:
131                 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
132                        dfu_get_layout(dfu->layout));
133                 return -1;
134         }
135
136         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
158         if (op == DFU_OP_WRITE)
159                 sprintf(cmd_buf + strlen(cmd_buf), " %lx", *len);
160
161         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
169         if (op != DFU_OP_WRITE) {
170                 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
181 int dfu_write_medium_mmc(struct dfu_entity *dfu,
182                 u64 offset, void *buf, long *len)
183 {
184         int ret = -1;
185
186         switch (dfu->layout) {
187         case DFU_RAW_ADDR:
188                 ret = mmc_block_op(DFU_OP_WRITE, dfu, offset, buf, len);
189                 break;
190         case DFU_FS_FAT:
191         case DFU_FS_EXT4:
192                 ret = mmc_file_buffer(dfu, buf, len);
193                 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
202 int 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
218 long 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
239 int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf,
240                 long *len)
241 {
242         int ret = -1;
243
244         switch (dfu->layout) {
245         case DFU_RAW_ADDR:
246                 ret = mmc_block_op(DFU_OP_READ, dfu, offset, buf, len);
247                 break;
248         case DFU_FS_FAT:
249         case DFU_FS_EXT4:
250                 ret = mmc_file_op(DFU_OP_READ, dfu, buf, len);
251                 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
260 /*
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
270  *      4th (optional):
271  *              mmcpart <num> (access to HW eMMC partitions)
272  */
273 int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *devstr, char *s)
274 {
275         const char *entity_type;
276         size_t second_arg;
277         size_t third_arg;
278
279         struct mmc *mmc;
280
281         const char *argv[3];
282         const char **parg = argv;
283
284         dfu->data.mmc.dev_num = simple_strtoul(devstr, NULL, 10);
285
286         for (; parg < argv + sizeof(argv) / sizeof(*argv); ++parg) {
287                 *parg = strsep(&s, " ");
288                 if (*parg == NULL) {
289                         error("Invalid number of arguments.\n");
290                         return -ENODEV;
291                 }
292         }
293
294         entity_type = argv[0];
295         /*
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);
301
302         mmc = find_mmc_device(dfu->data.mmc.dev_num);
303         if (mmc == NULL) {
304                 error("Couldn't find MMC device no. %d.\n",
305                       dfu->data.mmc.dev_num);
306                 return -ENODEV;
307         }
308
309         if (mmc_init(mmc)) {
310                 error("Couldn't init MMC device.\n");
311                 return -ENODEV;
312         }
313
314         dfu->data.mmc.hw_partition = -EINVAL;
315         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;
320
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
330         } 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;
335
336                 if (get_partition_info(blk_dev, mmcpart, &partinfo) != 0) {
337                         error("Couldn't find part #%d on mmc device #%d\n",
338                               mmcpart, mmcdev);
339                         return -ENODEV;
340                 }
341
342                 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;
350         } else {
351                 error("Memory layout (%s) not supported!\n", entity_type);
352                 return -ENODEV;
353         }
354
355         /* 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;
359         }
360
361         dfu->dev_type = DFU_DEV_MMC;
362         dfu->get_medium_size = dfu_get_medium_size_mmc;
363         dfu->read_medium = dfu_read_medium_mmc;
364         dfu->write_medium = dfu_write_medium_mmc;
365         dfu->flush_medium = dfu_flush_medium_mmc;
366         dfu->inited = 0;
367
368         return 0;
369 }