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