]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - drivers/dfu/dfu_mmc.c
Merge branch 'u-boot/master' into u-boot-arm/master
[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
16 enum dfu_mmc_op {
17         DFU_OP_READ = 1,
18         DFU_OP_WRITE,
19 };
20
21 static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE)
22                                 dfu_file_buf[CONFIG_SYS_DFU_MAX_FILE_SIZE];
23 static long dfu_file_buf_len;
24
25 static int mmc_block_op(enum dfu_mmc_op op, struct dfu_entity *dfu,
26                         u64 offset, void *buf, long *len)
27 {
28         char cmd_buf[DFU_CMD_BUF_SIZE];
29         u32 blk_start, blk_count;
30
31         /*
32          * We must ensure that we work in lba_blk_size chunks, so ALIGN
33          * this value.
34          */
35         *len = ALIGN(*len, dfu->data.mmc.lba_blk_size);
36
37         blk_start = dfu->data.mmc.lba_start +
38                         (u32)lldiv(offset, dfu->data.mmc.lba_blk_size);
39         blk_count = *len / dfu->data.mmc.lba_blk_size;
40         if (blk_start + blk_count >
41                         dfu->data.mmc.lba_start + dfu->data.mmc.lba_size) {
42                 puts("Request would exceed designated area!\n");
43                 return -EINVAL;
44         }
45
46         sprintf(cmd_buf, "mmc %s %p %x %x",
47                 op == DFU_OP_READ ? "read" : "write",
48                  buf, blk_start, blk_count);
49
50         debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
51         return run_command(cmd_buf, 0);
52 }
53
54 static int mmc_file_buffer(struct dfu_entity *dfu, void *buf, long *len)
55 {
56         if (dfu_file_buf_len + *len > CONFIG_SYS_DFU_MAX_FILE_SIZE) {
57                 dfu_file_buf_len = 0;
58                 return -EINVAL;
59         }
60
61         /* Add to the current buffer. */
62         memcpy(dfu_file_buf + dfu_file_buf_len, buf, *len);
63         dfu_file_buf_len += *len;
64
65         return 0;
66 }
67
68 static int mmc_file_op(enum dfu_mmc_op op, struct dfu_entity *dfu,
69                         void *buf, long *len)
70 {
71         char cmd_buf[DFU_CMD_BUF_SIZE];
72         char *str_env;
73         int ret;
74
75         switch (dfu->layout) {
76         case DFU_FS_FAT:
77                 sprintf(cmd_buf, "fat%s mmc %d:%d 0x%x %s",
78                         op == DFU_OP_READ ? "load" : "write",
79                         dfu->data.mmc.dev, dfu->data.mmc.part,
80                         (unsigned int) buf, dfu->name);
81                 if (op == DFU_OP_WRITE)
82                         sprintf(cmd_buf + strlen(cmd_buf), " %lx", *len);
83                 break;
84         case DFU_FS_EXT4:
85                 sprintf(cmd_buf, "ext4%s mmc %d:%d 0x%x /%s",
86                         op == DFU_OP_READ ? "load" : "write",
87                         dfu->data.mmc.dev, dfu->data.mmc.part,
88                         (unsigned int) buf, dfu->name);
89                 if (op == DFU_OP_WRITE)
90                         sprintf(cmd_buf + strlen(cmd_buf), " %ld", *len);
91                 break;
92         default:
93                 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
94                        dfu_get_layout(dfu->layout));
95                 return -1;
96         }
97
98         debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
99
100         ret = run_command(cmd_buf, 0);
101         if (ret) {
102                 puts("dfu: Read error!\n");
103                 return ret;
104         }
105
106         if (dfu->layout != DFU_RAW_ADDR && op == DFU_OP_READ) {
107                 str_env = getenv("filesize");
108                 if (str_env == NULL) {
109                         puts("dfu: Wrong file size!\n");
110                         return -1;
111                 }
112                 *len = simple_strtoul(str_env, NULL, 16);
113         }
114
115         return ret;
116 }
117
118 int dfu_write_medium_mmc(struct dfu_entity *dfu,
119                 u64 offset, void *buf, long *len)
120 {
121         int ret = -1;
122
123         switch (dfu->layout) {
124         case DFU_RAW_ADDR:
125                 ret = mmc_block_op(DFU_OP_WRITE, dfu, offset, buf, len);
126                 break;
127         case DFU_FS_FAT:
128         case DFU_FS_EXT4:
129                 ret = mmc_file_buffer(dfu, buf, len);
130                 break;
131         default:
132                 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
133                        dfu_get_layout(dfu->layout));
134         }
135
136         return ret;
137 }
138
139 int dfu_flush_medium_mmc(struct dfu_entity *dfu)
140 {
141         int ret = 0;
142
143         if (dfu->layout != DFU_RAW_ADDR) {
144                 /* Do stuff here. */
145                 ret = mmc_file_op(DFU_OP_WRITE, dfu, &dfu_file_buf,
146                                 &dfu_file_buf_len);
147
148                 /* Now that we're done */
149                 dfu_file_buf_len = 0;
150         }
151
152         return ret;
153 }
154
155 int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf,
156                 long *len)
157 {
158         int ret = -1;
159
160         switch (dfu->layout) {
161         case DFU_RAW_ADDR:
162                 ret = mmc_block_op(DFU_OP_READ, dfu, offset, buf, len);
163                 break;
164         case DFU_FS_FAT:
165         case DFU_FS_EXT4:
166                 ret = mmc_file_op(DFU_OP_READ, dfu, buf, len);
167                 break;
168         default:
169                 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
170                        dfu_get_layout(dfu->layout));
171         }
172
173         return ret;
174 }
175
176 int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s)
177 {
178         int dev, part;
179         struct mmc *mmc;
180         block_dev_desc_t *blk_dev;
181         disk_partition_t partinfo;
182         char *st;
183
184         dfu->dev_type = DFU_DEV_MMC;
185         st = strsep(&s, " ");
186         if (!strcmp(st, "mmc")) {
187                 dfu->layout = DFU_RAW_ADDR;
188                 dfu->data.mmc.lba_start = simple_strtoul(s, &s, 16);
189                 dfu->data.mmc.lba_size = simple_strtoul(++s, &s, 16);
190                 dfu->data.mmc.lba_blk_size = get_mmc_blk_size(dfu->dev_num);
191         } else if (!strcmp(st, "fat")) {
192                 dfu->layout = DFU_FS_FAT;
193         } else if (!strcmp(st, "ext4")) {
194                 dfu->layout = DFU_FS_EXT4;
195         } else if (!strcmp(st, "part")) {
196
197                 dfu->layout = DFU_RAW_ADDR;
198
199                 dev = simple_strtoul(s, &s, 10);
200                 s++;
201                 part = simple_strtoul(s, &s, 10);
202
203                 mmc = find_mmc_device(dev);
204                 if (mmc == NULL || mmc_init(mmc)) {
205                         printf("%s: could not find mmc device #%d!\n",
206                                __func__, dev);
207                         return -ENODEV;
208                 }
209
210                 blk_dev = &mmc->block_dev;
211                 if (get_partition_info(blk_dev, part, &partinfo) != 0) {
212                         printf("%s: could not find partition #%d on mmc device #%d!\n",
213                                __func__, part, dev);
214                         return -ENODEV;
215                 }
216
217                 dfu->data.mmc.lba_start = partinfo.start;
218                 dfu->data.mmc.lba_size = partinfo.size;
219                 dfu->data.mmc.lba_blk_size = partinfo.blksz;
220
221         } else {
222                 printf("%s: Memory layout (%s) not supported!\n", __func__, st);
223                 return -ENODEV;
224         }
225
226         if (dfu->layout == DFU_FS_EXT4 || dfu->layout == DFU_FS_FAT) {
227                 dfu->data.mmc.dev = simple_strtoul(s, &s, 10);
228                 dfu->data.mmc.part = simple_strtoul(++s, &s, 10);
229         }
230
231         dfu->read_medium = dfu_read_medium_mmc;
232         dfu->write_medium = dfu_write_medium_mmc;
233         dfu->flush_medium = dfu_flush_medium_mmc;
234
235         /* initial state */
236         dfu->inited = 0;
237
238         return 0;
239 }