1 // SPDX-License-Identifier: GPL-2.0+
3 * dfu_mtd.c -- DFU for MTD device.
5 * Copyright (C) 2019,STMicroelectronics - All Rights Reserved
13 #include <jffs2/load_kernel.h>
15 static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size)
17 return !do_div(size, mtd->erasesize);
20 static int mtd_block_op(enum dfu_op op, struct dfu_entity *dfu,
21 u64 offset, void *buf, long *len)
23 u64 off, lim, remaining;
24 struct mtd_info *mtd = dfu->data.mtd.info;
25 struct mtd_oob_ops io_op = {};
27 bool has_pages = mtd->type == MTD_NANDFLASH ||
28 mtd->type == MTD_MLCNANDFLASH;
30 /* if buf == NULL return total size of the area */
32 *len = dfu->data.mtd.size;
36 off = dfu->data.mtd.start + offset + dfu->bad_skip;
37 lim = dfu->data.mtd.start + dfu->data.mtd.size;
40 printf("Limit reached 0x%llx\n", lim);
42 return op == DFU_OP_READ ? 0 : -EIO;
44 /* limit request with the available size */
45 if (off + *len >= lim)
48 if (!mtd_is_aligned_with_block_size(mtd, off)) {
49 printf("Offset not aligned with a block (0x%x)\n",
55 if (op == DFU_OP_WRITE) {
56 struct erase_info erase_op = {};
58 remaining = round_up(*len, mtd->erasesize);
61 erase_op.len = mtd->erasesize;
65 if (erase_op.addr + remaining > lim) {
66 printf("Limit reached 0x%llx while erasing at offset 0x%llx\n",
71 ret = mtd_erase(mtd, &erase_op);
74 /* Abort if its not a bad block error */
76 printf("Failure while erasing at offset 0x%llx\n",
80 printf("Skipping bad block at 0x%08llx\n",
83 remaining -= mtd->erasesize;
86 /* Continue erase behind bad block */
87 erase_op.addr += mtd->erasesize;
91 io_op.mode = MTD_OPS_AUTO_OOB;
93 if (has_pages && io_op.len > mtd->writesize)
94 io_op.len = mtd->writesize;
99 /* Loop over to do the actual read/write */
102 if (off + remaining > lim) {
103 printf("Limit reached 0x%llx while %s at offset 0x%llx\n",
104 lim, op == DFU_OP_READ ? "reading" : "writing",
106 if (op == DFU_OP_READ) {
114 /* Skip the block if it is bad */
115 if (mtd_is_aligned_with_block_size(mtd, off) &&
116 mtd_block_isbad(mtd, off)) {
117 off += mtd->erasesize;
118 dfu->bad_skip += mtd->erasesize;
122 if (op == DFU_OP_READ)
123 ret = mtd_read_oob(mtd, off, &io_op);
125 ret = mtd_write_oob(mtd, off, &io_op);
128 printf("Failure while %s at offset 0x%llx\n",
129 op == DFU_OP_READ ? "reading" : "writing", off);
134 remaining -= io_op.retlen;
135 io_op.datbuf += io_op.retlen;
136 io_op.len = remaining;
137 if (has_pages && io_op.len > mtd->writesize)
138 io_op.len = mtd->writesize;
144 static int dfu_get_medium_size_mtd(struct dfu_entity *dfu, u64 *size)
146 *size = dfu->data.mtd.info->size;
151 static int dfu_read_medium_mtd(struct dfu_entity *dfu, u64 offset, void *buf,
156 switch (dfu->layout) {
158 ret = mtd_block_op(DFU_OP_READ, dfu, offset, buf, len);
161 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
162 dfu_get_layout(dfu->layout));
168 static int dfu_write_medium_mtd(struct dfu_entity *dfu,
169 u64 offset, void *buf, long *len)
173 switch (dfu->layout) {
175 ret = mtd_block_op(DFU_OP_WRITE, dfu, offset, buf, len);
178 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
179 dfu_get_layout(dfu->layout));
185 static int dfu_flush_medium_mtd(struct dfu_entity *dfu)
187 struct mtd_info *mtd = dfu->data.mtd.info;
191 /* in case of ubi partition, erase rest of the partition */
192 if (dfu->data.nand.ubi) {
193 struct erase_info erase_op = {};
195 erase_op.mtd = dfu->data.mtd.info;
196 erase_op.addr = round_up(dfu->data.mtd.start + dfu->offset +
197 dfu->bad_skip, mtd->erasesize);
198 erase_op.len = mtd->erasesize;
201 remaining = dfu->data.mtd.start + dfu->data.mtd.size -
205 ret = mtd_erase(mtd, &erase_op);
208 /* Abort if its not a bad block error */
211 printf("Skipping bad block at 0x%08llx\n",
215 /* Skip bad block and continue behind it */
216 erase_op.addr += mtd->erasesize;
217 remaining -= mtd->erasesize;
223 static unsigned int dfu_polltimeout_mtd(struct dfu_entity *dfu)
226 * Currently, Poll Timeout != 0 is only needed on nand
227 * ubi partition, as sectors which are not used need
230 if (dfu->data.nand.ubi)
231 return DFU_MANIFEST_POLL_TIMEOUT;
233 return DFU_DEFAULT_POLL_TIMEOUT;
236 int dfu_fill_entity_mtd(struct dfu_entity *dfu, char *devstr, char *s)
239 struct mtd_info *mtd;
243 mtd = get_mtd_device_nm(devstr);
244 if (IS_ERR_OR_NULL(mtd))
248 dfu->dev_type = DFU_DEV_MTD;
249 dfu->data.mtd.info = mtd;
251 has_pages = mtd->type == MTD_NANDFLASH || mtd->type == MTD_MLCNANDFLASH;
252 dfu->max_buf_size = has_pages ? mtd->erasesize : 0;
254 st = strsep(&s, " ");
255 if (!strcmp(st, "raw")) {
256 dfu->layout = DFU_RAW_ADDR;
257 dfu->data.mtd.start = simple_strtoul(s, &s, 16);
259 dfu->data.mtd.size = simple_strtoul(s, &s, 16);
260 } else if ((!strcmp(st, "part")) || (!strcmp(st, "partubi"))) {
262 struct mtd_device *mtd_dev;
264 struct part_info *pi;
266 dfu->layout = DFU_RAW_ADDR;
268 part = simple_strtoul(s, &s, 10);
270 sprintf(mtd_id, "%s,%d", devstr, part - 1);
271 printf("using id '%s'\n", mtd_id);
275 ret = find_dev_and_part(mtd_id, &mtd_dev, &part_num, &pi);
277 printf("Could not locate '%s'\n", mtd_id);
281 dfu->data.mtd.start = pi->offset;
282 dfu->data.mtd.size = pi->size;
283 if (!strcmp(st, "partubi"))
284 dfu->data.mtd.ubi = 1;
286 printf("%s: Memory layout (%s) not supported!\n", __func__, st);
290 if (!mtd_is_aligned_with_block_size(mtd, dfu->data.mtd.start)) {
291 printf("Offset not aligned with a block (0x%x)\n",
295 if (!mtd_is_aligned_with_block_size(mtd, dfu->data.mtd.size)) {
296 printf("Size not aligned with a block (0x%x)\n",
301 dfu->get_medium_size = dfu_get_medium_size_mtd;
302 dfu->read_medium = dfu_read_medium_mtd;
303 dfu->write_medium = dfu_write_medium_mtd;
304 dfu->flush_medium = dfu_flush_medium_mtd;
305 dfu->poll_timeout = dfu_polltimeout_mtd;