xref: /rk3399_rockchip-uboot/drivers/dfu/dfu_mmc.c (revision b7d4259af298402b7d65c876d8e39e5b9e6c8934)
1cb383cd2SLukasz Majewski /*
2cb383cd2SLukasz Majewski  * dfu.c -- DFU back-end routines
3cb383cd2SLukasz Majewski  *
4cb383cd2SLukasz Majewski  * Copyright (C) 2012 Samsung Electronics
5cb383cd2SLukasz Majewski  * author: Lukasz Majewski <l.majewski@samsung.com>
6cb383cd2SLukasz Majewski  *
71a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
8cb383cd2SLukasz Majewski  */
9cb383cd2SLukasz Majewski 
10cb383cd2SLukasz Majewski #include <common.h>
11cb383cd2SLukasz Majewski #include <malloc.h>
121b6ca18bSPantelis Antoniou #include <errno.h>
13ea2453d5SPantelis Antoniou #include <div64.h>
14cb383cd2SLukasz Majewski #include <dfu.h>
157d0b605aSŁukasz Majewski #include <mmc.h>
16cb383cd2SLukasz Majewski 
17ea2453d5SPantelis Antoniou static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE)
18ea2453d5SPantelis Antoniou 				dfu_file_buf[CONFIG_SYS_DFU_MAX_FILE_SIZE];
19ea2453d5SPantelis Antoniou static long dfu_file_buf_len;
20ea2453d5SPantelis Antoniou 
215a127c84SAfzal Mohammed static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu,
22ea2453d5SPantelis Antoniou 			u64 offset, void *buf, long *len)
23cb383cd2SLukasz Majewski {
247d0b605aSŁukasz Majewski 	struct mmc *mmc = find_mmc_device(dfu->dev_num);
257d0b605aSŁukasz Majewski 	u32 blk_start, blk_count, n = 0;
26cb383cd2SLukasz Majewski 
27ea2453d5SPantelis Antoniou 	/*
28ea2453d5SPantelis Antoniou 	 * We must ensure that we work in lba_blk_size chunks, so ALIGN
29ea2453d5SPantelis Antoniou 	 * this value.
30ea2453d5SPantelis Antoniou 	 */
31ea2453d5SPantelis Antoniou 	*len = ALIGN(*len, dfu->data.mmc.lba_blk_size);
32ea2453d5SPantelis Antoniou 
33ea2453d5SPantelis Antoniou 	blk_start = dfu->data.mmc.lba_start +
34ea2453d5SPantelis Antoniou 			(u32)lldiv(offset, dfu->data.mmc.lba_blk_size);
35ea2453d5SPantelis Antoniou 	blk_count = *len / dfu->data.mmc.lba_blk_size;
36ea2453d5SPantelis Antoniou 	if (blk_start + blk_count >
37ea2453d5SPantelis Antoniou 			dfu->data.mmc.lba_start + dfu->data.mmc.lba_size) {
38ea2453d5SPantelis Antoniou 		puts("Request would exceed designated area!\n");
39ea2453d5SPantelis Antoniou 		return -EINVAL;
40ea2453d5SPantelis Antoniou 	}
41ea2453d5SPantelis Antoniou 
427d0b605aSŁukasz Majewski 	debug("%s: %s dev: %d start: %d cnt: %d buf: 0x%p\n", __func__,
437d0b605aSŁukasz Majewski 	      op == DFU_OP_READ ? "MMC READ" : "MMC WRITE", dfu->dev_num,
447d0b605aSŁukasz Majewski 	      blk_start, blk_count, buf);
457d0b605aSŁukasz Majewski 	switch (op) {
467d0b605aSŁukasz Majewski 	case DFU_OP_READ:
477d0b605aSŁukasz Majewski 		n = mmc->block_dev.block_read(dfu->dev_num, blk_start,
487d0b605aSŁukasz Majewski 					      blk_count, buf);
497d0b605aSŁukasz Majewski 		break;
507d0b605aSŁukasz Majewski 	case DFU_OP_WRITE:
517d0b605aSŁukasz Majewski 		n = mmc->block_dev.block_write(dfu->dev_num, blk_start,
527d0b605aSŁukasz Majewski 					       blk_count, buf);
537d0b605aSŁukasz Majewski 		break;
547d0b605aSŁukasz Majewski 	default:
557d0b605aSŁukasz Majewski 		error("Operation not supported\n");
567d0b605aSŁukasz Majewski 	}
57cb383cd2SLukasz Majewski 
587d0b605aSŁukasz Majewski 	if (n != blk_count) {
597d0b605aSŁukasz Majewski 		error("MMC operation failed");
607d0b605aSŁukasz Majewski 		return -EIO;
617d0b605aSŁukasz Majewski 	}
627d0b605aSŁukasz Majewski 
637d0b605aSŁukasz Majewski 	return 0;
64cb383cd2SLukasz Majewski }
65cb383cd2SLukasz Majewski 
66ea2453d5SPantelis Antoniou static int mmc_file_buffer(struct dfu_entity *dfu, void *buf, long *len)
67cb383cd2SLukasz Majewski {
68ea2453d5SPantelis Antoniou 	if (dfu_file_buf_len + *len > CONFIG_SYS_DFU_MAX_FILE_SIZE) {
69ea2453d5SPantelis Antoniou 		dfu_file_buf_len = 0;
70ea2453d5SPantelis Antoniou 		return -EINVAL;
71cb383cd2SLukasz Majewski 	}
72cb383cd2SLukasz Majewski 
73ea2453d5SPantelis Antoniou 	/* Add to the current buffer. */
74ea2453d5SPantelis Antoniou 	memcpy(dfu_file_buf + dfu_file_buf_len, buf, *len);
75ea2453d5SPantelis Antoniou 	dfu_file_buf_len += *len;
76ea2453d5SPantelis Antoniou 
77ea2453d5SPantelis Antoniou 	return 0;
78cb383cd2SLukasz Majewski }
79cb383cd2SLukasz Majewski 
805a127c84SAfzal Mohammed static int mmc_file_op(enum dfu_op op, struct dfu_entity *dfu,
81cb383cd2SLukasz Majewski 			void *buf, long *len)
82cb383cd2SLukasz Majewski {
83cb383cd2SLukasz Majewski 	char cmd_buf[DFU_CMD_BUF_SIZE];
84cb383cd2SLukasz Majewski 	char *str_env;
85cb383cd2SLukasz Majewski 	int ret;
86cb383cd2SLukasz Majewski 
8743e66272SŁukasz Majewski 	switch (dfu->layout) {
8843e66272SŁukasz Majewski 	case DFU_FS_FAT:
89ea2453d5SPantelis Antoniou 		sprintf(cmd_buf, "fat%s mmc %d:%d 0x%x %s",
90cb383cd2SLukasz Majewski 			op == DFU_OP_READ ? "load" : "write",
91cb383cd2SLukasz Majewski 			dfu->data.mmc.dev, dfu->data.mmc.part,
92ea2453d5SPantelis Antoniou 			(unsigned int) buf, dfu->name);
9343e66272SŁukasz Majewski 		break;
9443e66272SŁukasz Majewski 	case DFU_FS_EXT4:
95ea2453d5SPantelis Antoniou 		sprintf(cmd_buf, "ext4%s mmc %d:%d 0x%x /%s",
9643e66272SŁukasz Majewski 			op == DFU_OP_READ ? "load" : "write",
9743e66272SŁukasz Majewski 			dfu->data.mmc.dev, dfu->data.mmc.part,
98ea2453d5SPantelis Antoniou 			(unsigned int) buf, dfu->name);
9943e66272SŁukasz Majewski 		break;
10043e66272SŁukasz Majewski 	default:
10143e66272SŁukasz Majewski 		printf("%s: Layout (%s) not (yet) supported!\n", __func__,
10243e66272SŁukasz Majewski 		       dfu_get_layout(dfu->layout));
103ea2453d5SPantelis Antoniou 		return -1;
10443e66272SŁukasz Majewski 	}
105cb383cd2SLukasz Majewski 
10617eb1d8fSLukasz Majewski 	if (op == DFU_OP_WRITE)
10717eb1d8fSLukasz Majewski 		sprintf(cmd_buf + strlen(cmd_buf), " %lx", *len);
10817eb1d8fSLukasz Majewski 
109cb383cd2SLukasz Majewski 	debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
110cb383cd2SLukasz Majewski 
111cb383cd2SLukasz Majewski 	ret = run_command(cmd_buf, 0);
112cb383cd2SLukasz Majewski 	if (ret) {
113cb383cd2SLukasz Majewski 		puts("dfu: Read error!\n");
114cb383cd2SLukasz Majewski 		return ret;
115cb383cd2SLukasz Majewski 	}
116cb383cd2SLukasz Majewski 
11781c1d7b6SŁukasz Majewski 	if (dfu->layout != DFU_RAW_ADDR && op == DFU_OP_READ) {
118cb383cd2SLukasz Majewski 		str_env = getenv("filesize");
119cb383cd2SLukasz Majewski 		if (str_env == NULL) {
120cb383cd2SLukasz Majewski 			puts("dfu: Wrong file size!\n");
121cb383cd2SLukasz Majewski 			return -1;
122cb383cd2SLukasz Majewski 		}
123cb383cd2SLukasz Majewski 		*len = simple_strtoul(str_env, NULL, 16);
124cb383cd2SLukasz Majewski 	}
125cb383cd2SLukasz Majewski 
126cb383cd2SLukasz Majewski 	return ret;
127cb383cd2SLukasz Majewski }
128cb383cd2SLukasz Majewski 
129ea2453d5SPantelis Antoniou int dfu_write_medium_mmc(struct dfu_entity *dfu,
130ea2453d5SPantelis Antoniou 		u64 offset, void *buf, long *len)
131cb383cd2SLukasz Majewski {
132cb383cd2SLukasz Majewski 	int ret = -1;
133cb383cd2SLukasz Majewski 
134cb383cd2SLukasz Majewski 	switch (dfu->layout) {
135cb383cd2SLukasz Majewski 	case DFU_RAW_ADDR:
136ea2453d5SPantelis Antoniou 		ret = mmc_block_op(DFU_OP_WRITE, dfu, offset, buf, len);
137cb383cd2SLukasz Majewski 		break;
138cb383cd2SLukasz Majewski 	case DFU_FS_FAT:
13943e66272SŁukasz Majewski 	case DFU_FS_EXT4:
140ea2453d5SPantelis Antoniou 		ret = mmc_file_buffer(dfu, buf, len);
141cb383cd2SLukasz Majewski 		break;
142cb383cd2SLukasz Majewski 	default:
143cb383cd2SLukasz Majewski 		printf("%s: Layout (%s) not (yet) supported!\n", __func__,
144cb383cd2SLukasz Majewski 		       dfu_get_layout(dfu->layout));
145cb383cd2SLukasz Majewski 	}
146cb383cd2SLukasz Majewski 
147cb383cd2SLukasz Majewski 	return ret;
148cb383cd2SLukasz Majewski }
149cb383cd2SLukasz Majewski 
150ea2453d5SPantelis Antoniou int dfu_flush_medium_mmc(struct dfu_entity *dfu)
151ea2453d5SPantelis Antoniou {
152ea2453d5SPantelis Antoniou 	int ret = 0;
153ea2453d5SPantelis Antoniou 
154ea2453d5SPantelis Antoniou 	if (dfu->layout != DFU_RAW_ADDR) {
155ea2453d5SPantelis Antoniou 		/* Do stuff here. */
156ea2453d5SPantelis Antoniou 		ret = mmc_file_op(DFU_OP_WRITE, dfu, &dfu_file_buf,
157ea2453d5SPantelis Antoniou 				&dfu_file_buf_len);
158ea2453d5SPantelis Antoniou 
159ea2453d5SPantelis Antoniou 		/* Now that we're done */
160ea2453d5SPantelis Antoniou 		dfu_file_buf_len = 0;
161ea2453d5SPantelis Antoniou 	}
162ea2453d5SPantelis Antoniou 
163ea2453d5SPantelis Antoniou 	return ret;
164ea2453d5SPantelis Antoniou }
165ea2453d5SPantelis Antoniou 
166ea2453d5SPantelis Antoniou int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf,
167ea2453d5SPantelis Antoniou 		long *len)
168cb383cd2SLukasz Majewski {
169cb383cd2SLukasz Majewski 	int ret = -1;
170cb383cd2SLukasz Majewski 
171cb383cd2SLukasz Majewski 	switch (dfu->layout) {
172cb383cd2SLukasz Majewski 	case DFU_RAW_ADDR:
173ea2453d5SPantelis Antoniou 		ret = mmc_block_op(DFU_OP_READ, dfu, offset, buf, len);
174cb383cd2SLukasz Majewski 		break;
175cb383cd2SLukasz Majewski 	case DFU_FS_FAT:
17643e66272SŁukasz Majewski 	case DFU_FS_EXT4:
177ea2453d5SPantelis Antoniou 		ret = mmc_file_op(DFU_OP_READ, dfu, buf, len);
178cb383cd2SLukasz Majewski 		break;
179cb383cd2SLukasz Majewski 	default:
180cb383cd2SLukasz Majewski 		printf("%s: Layout (%s) not (yet) supported!\n", __func__,
181cb383cd2SLukasz Majewski 		       dfu_get_layout(dfu->layout));
182cb383cd2SLukasz Majewski 	}
183cb383cd2SLukasz Majewski 
184cb383cd2SLukasz Majewski 	return ret;
185cb383cd2SLukasz Majewski }
186cb383cd2SLukasz Majewski 
187711b931fSMateusz Zalega /*
188711b931fSMateusz Zalega  * @param s Parameter string containing space-separated arguments:
189711b931fSMateusz Zalega  *	1st:
190711b931fSMateusz Zalega  *		raw	(raw read/write)
191711b931fSMateusz Zalega  *		fat	(files)
192711b931fSMateusz Zalega  *		ext4	(^)
193711b931fSMateusz Zalega  *		part	(partition image)
194711b931fSMateusz Zalega  *	2nd and 3rd:
195711b931fSMateusz Zalega  *		lba_start and lba_size, for raw write
196711b931fSMateusz Zalega  *		mmc_dev and mmc_part, for filesystems and part
197711b931fSMateusz Zalega  */
198cb383cd2SLukasz Majewski int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s)
199cb383cd2SLukasz Majewski {
200711b931fSMateusz Zalega 	const char *entity_type;
201711b931fSMateusz Zalega 	size_t second_arg;
202711b931fSMateusz Zalega 	size_t third_arg;
203711b931fSMateusz Zalega 
2041b6ca18bSPantelis Antoniou 	struct mmc *mmc;
205711b931fSMateusz Zalega 
206711b931fSMateusz Zalega 	const char *argv[3];
207711b931fSMateusz Zalega 	const char **parg = argv;
208711b931fSMateusz Zalega 
209711b931fSMateusz Zalega 	for (; parg < argv + sizeof(argv) / sizeof(*argv); ++parg) {
210711b931fSMateusz Zalega 		*parg = strsep(&s, " ");
211711b931fSMateusz Zalega 		if (*parg == NULL) {
212711b931fSMateusz Zalega 			error("Invalid number of arguments.\n");
213711b931fSMateusz Zalega 			return -ENODEV;
214711b931fSMateusz Zalega 		}
215711b931fSMateusz Zalega 	}
216711b931fSMateusz Zalega 
217711b931fSMateusz Zalega 	entity_type = argv[0];
218*b7d4259aSMateusz Zalega 	/*
219*b7d4259aSMateusz Zalega 	 * Base 0 means we'll accept (prefixed with 0x or 0) base 16, 8,
220*b7d4259aSMateusz Zalega 	 * with default 10.
221*b7d4259aSMateusz Zalega 	 */
222*b7d4259aSMateusz Zalega 	second_arg = simple_strtoul(argv[1], NULL, 0);
223*b7d4259aSMateusz Zalega 	third_arg = simple_strtoul(argv[2], NULL, 0);
224711b931fSMateusz Zalega 
225711b931fSMateusz Zalega 	mmc = find_mmc_device(dfu->dev_num);
226711b931fSMateusz Zalega 	if (mmc == NULL) {
227711b931fSMateusz Zalega 		error("Couldn't find MMC device no. %d.\n", dfu->dev_num);
228711b931fSMateusz Zalega 		return -ENODEV;
229711b931fSMateusz Zalega 	}
230711b931fSMateusz Zalega 
231711b931fSMateusz Zalega 	if (mmc_init(mmc)) {
232711b931fSMateusz Zalega 		error("Couldn't init MMC device.\n");
233711b931fSMateusz Zalega 		return -ENODEV;
234711b931fSMateusz Zalega 	}
235711b931fSMateusz Zalega 
236711b931fSMateusz Zalega 	if (!strcmp(entity_type, "raw")) {
237711b931fSMateusz Zalega 		dfu->layout			= DFU_RAW_ADDR;
238711b931fSMateusz Zalega 		dfu->data.mmc.lba_start		= second_arg;
239711b931fSMateusz Zalega 		dfu->data.mmc.lba_size		= third_arg;
240711b931fSMateusz Zalega 		dfu->data.mmc.lba_blk_size	= mmc->read_bl_len;
241711b931fSMateusz Zalega 	} else if (!strcmp(entity_type, "part")) {
2421b6ca18bSPantelis Antoniou 		disk_partition_t partinfo;
243711b931fSMateusz Zalega 		block_dev_desc_t *blk_dev = &mmc->block_dev;
244711b931fSMateusz Zalega 		int mmcdev = second_arg;
245711b931fSMateusz Zalega 		int mmcpart = third_arg;
246cb383cd2SLukasz Majewski 
247711b931fSMateusz Zalega 		if (get_partition_info(blk_dev, mmcpart, &partinfo) != 0) {
248711b931fSMateusz Zalega 			error("Couldn't find part #%d on mmc device #%d\n",
249711b931fSMateusz Zalega 			      mmcpart, mmcdev);
2501b6ca18bSPantelis Antoniou 			return -ENODEV;
2511b6ca18bSPantelis Antoniou 		}
2521b6ca18bSPantelis Antoniou 
253711b931fSMateusz Zalega 		dfu->layout			= DFU_RAW_ADDR;
2541b6ca18bSPantelis Antoniou 		dfu->data.mmc.lba_start		= partinfo.start;
2551b6ca18bSPantelis Antoniou 		dfu->data.mmc.lba_size		= partinfo.size;
2561b6ca18bSPantelis Antoniou 		dfu->data.mmc.lba_blk_size	= partinfo.blksz;
257711b931fSMateusz Zalega 	} else if (!strcmp(entity_type, "fat")) {
258711b931fSMateusz Zalega 		dfu->layout = DFU_FS_FAT;
259711b931fSMateusz Zalega 	} else if (!strcmp(entity_type, "ext4")) {
260711b931fSMateusz Zalega 		dfu->layout = DFU_FS_EXT4;
261cb383cd2SLukasz Majewski 	} else {
262711b931fSMateusz Zalega 		error("Memory layout (%s) not supported!\n", entity_type);
2631b6ca18bSPantelis Antoniou 		return -ENODEV;
264cb383cd2SLukasz Majewski 	}
265cb383cd2SLukasz Majewski 
266711b931fSMateusz Zalega 	/* if it's NOT a raw write */
267711b931fSMateusz Zalega 	if (strcmp(entity_type, "raw")) {
268711b931fSMateusz Zalega 		dfu->data.mmc.dev = second_arg;
269711b931fSMateusz Zalega 		dfu->data.mmc.part = third_arg;
27043e66272SŁukasz Majewski 	}
27143e66272SŁukasz Majewski 
272711b931fSMateusz Zalega 	dfu->dev_type = DFU_DEV_MMC;
273cb383cd2SLukasz Majewski 	dfu->read_medium = dfu_read_medium_mmc;
274cb383cd2SLukasz Majewski 	dfu->write_medium = dfu_write_medium_mmc;
275ea2453d5SPantelis Antoniou 	dfu->flush_medium = dfu_flush_medium_mmc;
276ea2453d5SPantelis Antoniou 	dfu->inited = 0;
277cb383cd2SLukasz Majewski 
278cb383cd2SLukasz Majewski 	return 0;
279cb383cd2SLukasz Majewski }
280