]> git.karo-electronics.de Git - karo-tx-uboot.git/blob - drivers/dfu/dfu_mmc.c
compiler*.h: sync include/linux/compiler*.h with Linux 4.5-rc6
[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 *dfu_file_buf;
20 static long dfu_file_buf_len;
21
22 static int mmc_access_part(struct dfu_entity *dfu, struct mmc *mmc, int part)
23 {
24         int ret;
25
26         if (part == mmc->part_num)
27                 return 0;
28
29         ret = mmc_switch_part(dfu->data.mmc.dev_num, part);
30         if (ret) {
31                 error("Cannot switch to partition %d\n", part);
32                 return ret;
33         }
34         mmc->part_num = part;
35
36         return 0;
37 }
38
39 static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu,
40                         u64 offset, void *buf, long *len)
41 {
42         struct mmc *mmc;
43         u32 blk_start, blk_count, n = 0;
44         int ret, part_num_bkp = 0;
45
46         mmc = find_mmc_device(dfu->data.mmc.dev_num);
47         if (!mmc) {
48                 error("Device MMC %d - not found!", dfu->data.mmc.dev_num);
49                 return -ENODEV;
50         }
51
52         /*
53          * We must ensure that we work in lba_blk_size chunks, so ALIGN
54          * this value.
55          */
56         *len = ALIGN(*len, dfu->data.mmc.lba_blk_size);
57
58         blk_start = dfu->data.mmc.lba_start +
59                         (u32)lldiv(offset, dfu->data.mmc.lba_blk_size);
60         blk_count = *len / dfu->data.mmc.lba_blk_size;
61         if (blk_start + blk_count >
62                         dfu->data.mmc.lba_start + dfu->data.mmc.lba_size) {
63                 puts("Request would exceed designated area!\n");
64                 return -EINVAL;
65         }
66
67         if (dfu->data.mmc.hw_partition >= 0) {
68                 part_num_bkp = mmc->part_num;
69                 ret = mmc_access_part(dfu, mmc, dfu->data.mmc.hw_partition);
70                 if (ret)
71                         return ret;
72         }
73
74         debug("%s: %s dev: %d start: %d cnt: %d buf: 0x%p\n", __func__,
75               op == DFU_OP_READ ? "MMC READ" : "MMC WRITE",
76               dfu->data.mmc.dev_num, blk_start, blk_count, buf);
77         switch (op) {
78         case DFU_OP_READ:
79                 n = mmc->block_dev.block_read(dfu->data.mmc.dev_num, blk_start,
80                                               blk_count, buf);
81                 break;
82         case DFU_OP_WRITE:
83                 n = mmc->block_dev.block_write(dfu->data.mmc.dev_num, blk_start,
84                                                blk_count, buf);
85                 break;
86         default:
87                 error("Operation not supported\n");
88         }
89
90         if (n != blk_count) {
91                 error("MMC operation failed");
92                 if (dfu->data.mmc.hw_partition >= 0)
93                         mmc_access_part(dfu, mmc, part_num_bkp);
94                 return -EIO;
95         }
96
97         if (dfu->data.mmc.hw_partition >= 0) {
98                 ret = mmc_access_part(dfu, mmc, part_num_bkp);
99                 if (ret)
100                         return ret;
101         }
102
103         return 0;
104 }
105
106 static int mmc_file_buffer(struct dfu_entity *dfu, void *buf, long *len)
107 {
108         if (dfu_file_buf_len + *len > CONFIG_SYS_DFU_MAX_FILE_SIZE) {
109                 dfu_file_buf_len = 0;
110                 return -EINVAL;
111         }
112
113         /* Add to the current buffer. */
114         memcpy(dfu_file_buf + dfu_file_buf_len, buf, *len);
115         dfu_file_buf_len += *len;
116
117         return 0;
118 }
119
120 static int mmc_file_op(enum dfu_op op, struct dfu_entity *dfu,
121                         void *buf, long *len)
122 {
123         const char *fsname, *opname;
124         char cmd_buf[DFU_CMD_BUF_SIZE];
125         char *str_env;
126         int ret;
127
128         switch (dfu->layout) {
129         case DFU_FS_FAT:
130                 fsname = "fat";
131                 break;
132         case DFU_FS_EXT4:
133                 fsname = "ext4";
134                 break;
135         default:
136                 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
137                        dfu_get_layout(dfu->layout));
138                 return -1;
139         }
140
141         switch (op) {
142         case DFU_OP_READ:
143                 opname = "load";
144                 break;
145         case DFU_OP_WRITE:
146                 opname = "write";
147                 break;
148         case DFU_OP_SIZE:
149                 opname = "size";
150                 break;
151         default:
152                 return -1;
153         }
154
155         sprintf(cmd_buf, "%s%s mmc %d:%d", fsname, opname,
156                 dfu->data.mmc.dev, dfu->data.mmc.part);
157
158         if (op != DFU_OP_SIZE)
159                 sprintf(cmd_buf + strlen(cmd_buf), " %p", buf);
160
161         sprintf(cmd_buf + strlen(cmd_buf), " %s", dfu->name);
162
163         if (op == DFU_OP_WRITE)
164                 sprintf(cmd_buf + strlen(cmd_buf), " %lx", *len);
165
166         debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
167
168         ret = run_command(cmd_buf, 0);
169         if (ret) {
170                 puts("dfu: Read error!\n");
171                 return ret;
172         }
173
174         if (op != DFU_OP_WRITE) {
175                 str_env = getenv("filesize");
176                 if (str_env == NULL) {
177                         puts("dfu: Wrong file size!\n");
178                         return -1;
179                 }
180                 *len = simple_strtoul(str_env, NULL, 16);
181         }
182
183         return ret;
184 }
185
186 int dfu_write_medium_mmc(struct dfu_entity *dfu,
187                 u64 offset, void *buf, long *len)
188 {
189         int ret = -1;
190
191         switch (dfu->layout) {
192         case DFU_RAW_ADDR:
193                 ret = mmc_block_op(DFU_OP_WRITE, dfu, offset, buf, len);
194                 break;
195         case DFU_FS_FAT:
196         case DFU_FS_EXT4:
197                 ret = mmc_file_buffer(dfu, buf, len);
198                 break;
199         default:
200                 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
201                        dfu_get_layout(dfu->layout));
202         }
203
204         return ret;
205 }
206
207 int dfu_flush_medium_mmc(struct dfu_entity *dfu)
208 {
209         int ret = 0;
210
211         if (dfu->layout != DFU_RAW_ADDR) {
212                 /* Do stuff here. */
213                 ret = mmc_file_op(DFU_OP_WRITE, dfu, dfu_file_buf,
214                                 &dfu_file_buf_len);
215
216                 /* Now that we're done */
217                 dfu_file_buf_len = 0;
218         }
219
220         return ret;
221 }
222
223 long dfu_get_medium_size_mmc(struct dfu_entity *dfu)
224 {
225         int ret;
226         long len;
227
228         switch (dfu->layout) {
229         case DFU_RAW_ADDR:
230                 return dfu->data.mmc.lba_size * dfu->data.mmc.lba_blk_size;
231         case DFU_FS_FAT:
232         case DFU_FS_EXT4:
233                 ret = mmc_file_op(DFU_OP_SIZE, dfu, NULL, &len);
234                 if (ret < 0)
235                         return ret;
236                 return len;
237         default:
238                 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
239                        dfu_get_layout(dfu->layout));
240                 return -1;
241         }
242 }
243
244 int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf,
245                 long *len)
246 {
247         int ret = -1;
248
249         switch (dfu->layout) {
250         case DFU_RAW_ADDR:
251                 ret = mmc_block_op(DFU_OP_READ, dfu, offset, buf, len);
252                 break;
253         case DFU_FS_FAT:
254         case DFU_FS_EXT4:
255                 ret = mmc_file_op(DFU_OP_READ, dfu, buf, len);
256                 break;
257         default:
258                 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
259                        dfu_get_layout(dfu->layout));
260         }
261
262         return ret;
263 }
264
265 void dfu_free_entity_mmc(struct dfu_entity *dfu)
266 {
267         if (dfu_file_buf) {
268                 free(dfu_file_buf);
269                 dfu_file_buf = NULL;
270         }
271 }
272
273 /*
274  * @param s Parameter string containing space-separated arguments:
275  *      1st:
276  *              raw     (raw read/write)
277  *              fat     (files)
278  *              ext4    (^)
279  *              part    (partition image)
280  *      2nd and 3rd:
281  *              lba_start and lba_size, for raw write
282  *              mmc_dev and mmc_part, for filesystems and part
283  *      4th (optional):
284  *              mmcpart <num> (access to HW eMMC partitions)
285  */
286 int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *devstr, char *s)
287 {
288         const char *entity_type;
289         size_t second_arg;
290         size_t third_arg;
291
292         struct mmc *mmc;
293
294         const char *argv[3];
295         const char **parg = argv;
296
297         dfu->data.mmc.dev_num = simple_strtoul(devstr, NULL, 10);
298
299         for (; parg < argv + sizeof(argv) / sizeof(*argv); ++parg) {
300                 *parg = strsep(&s, " ");
301                 if (*parg == NULL) {
302                         error("Invalid number of arguments.\n");
303                         return -ENODEV;
304                 }
305         }
306
307         entity_type = argv[0];
308         /*
309          * Base 0 means we'll accept (prefixed with 0x or 0) base 16, 8,
310          * with default 10.
311          */
312         second_arg = simple_strtoul(argv[1], NULL, 0);
313         third_arg = simple_strtoul(argv[2], NULL, 0);
314
315         mmc = find_mmc_device(dfu->data.mmc.dev_num);
316         if (mmc == NULL) {
317                 error("Couldn't find MMC device no. %d.\n",
318                       dfu->data.mmc.dev_num);
319                 return -ENODEV;
320         }
321
322         if (mmc_init(mmc)) {
323                 error("Couldn't init MMC device.\n");
324                 return -ENODEV;
325         }
326
327         dfu->data.mmc.hw_partition = -EINVAL;
328         if (!strcmp(entity_type, "raw")) {
329                 dfu->layout                     = DFU_RAW_ADDR;
330                 dfu->data.mmc.lba_start         = second_arg;
331                 dfu->data.mmc.lba_size          = third_arg;
332                 dfu->data.mmc.lba_blk_size      = mmc->read_bl_len;
333
334                 /*
335                  * Check for an extra entry at dfu_alt_info env variable
336                  * specifying the mmc HW defined partition number
337                  */
338                 if (s)
339                         if (!strcmp(strsep(&s, " "), "mmcpart"))
340                                 dfu->data.mmc.hw_partition =
341                                         simple_strtoul(s, NULL, 0);
342
343         } else if (!strcmp(entity_type, "part")) {
344                 disk_partition_t partinfo;
345                 block_dev_desc_t *blk_dev = &mmc->block_dev;
346                 int mmcdev = second_arg;
347                 int mmcpart = third_arg;
348
349                 if (get_partition_info(blk_dev, mmcpart, &partinfo) != 0) {
350                         error("Couldn't find part #%d on mmc device #%d\n",
351                               mmcpart, mmcdev);
352                         return -ENODEV;
353                 }
354
355                 dfu->layout                     = DFU_RAW_ADDR;
356                 dfu->data.mmc.lba_start         = partinfo.start;
357                 dfu->data.mmc.lba_size          = partinfo.size;
358                 dfu->data.mmc.lba_blk_size      = partinfo.blksz;
359         } else if (!strcmp(entity_type, "fat")) {
360                 dfu->layout = DFU_FS_FAT;
361         } else if (!strcmp(entity_type, "ext4")) {
362                 dfu->layout = DFU_FS_EXT4;
363         } else {
364                 error("Memory layout (%s) not supported!\n", entity_type);
365                 return -ENODEV;
366         }
367
368         /* if it's NOT a raw write */
369         if (strcmp(entity_type, "raw")) {
370                 dfu->data.mmc.dev = second_arg;
371                 dfu->data.mmc.part = third_arg;
372         }
373
374         dfu->dev_type = DFU_DEV_MMC;
375         dfu->get_medium_size = dfu_get_medium_size_mmc;
376         dfu->read_medium = dfu_read_medium_mmc;
377         dfu->write_medium = dfu_write_medium_mmc;
378         dfu->flush_medium = dfu_flush_medium_mmc;
379         dfu->inited = 0;
380         dfu->free_entity = dfu_free_entity_mmc;
381
382         /* Check if file buffer is ready */
383         if (!dfu_file_buf) {
384                 dfu_file_buf = memalign(CONFIG_SYS_CACHELINE_SIZE,
385                                         CONFIG_SYS_DFU_MAX_FILE_SIZE);
386                 if (!dfu_file_buf) {
387                         error("Could not memalign 0x%x bytes",
388                               CONFIG_SYS_DFU_MAX_FILE_SIZE);
389                         return -ENOMEM;
390                 }
391         }
392
393         return 0;
394 }