xref: /rk3399_rockchip-uboot/drivers/dfu/dfu_mmc.c (revision ea2453d56b8860dbd18a3c517531ffc8dcb5c839)
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  *
7cb383cd2SLukasz Majewski  * This program is free software; you can redistribute it and/or modify
8cb383cd2SLukasz Majewski  * it under the terms of the GNU General Public License as published by
9cb383cd2SLukasz Majewski  * the Free Software Foundation; either version 2 of the License, or
10cb383cd2SLukasz Majewski  * (at your option) any later version.
11cb383cd2SLukasz Majewski  *
12cb383cd2SLukasz Majewski  * This program is distributed in the hope that it will be useful,
13cb383cd2SLukasz Majewski  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14cb383cd2SLukasz Majewski  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15cb383cd2SLukasz Majewski  * GNU General Public License for more details.
16cb383cd2SLukasz Majewski  *
17cb383cd2SLukasz Majewski  * You should have received a copy of the GNU General Public License
18cb383cd2SLukasz Majewski  * along with this program; if not, write to the Free Software
19cb383cd2SLukasz Majewski  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20cb383cd2SLukasz Majewski  */
21cb383cd2SLukasz Majewski 
22cb383cd2SLukasz Majewski #include <common.h>
23cb383cd2SLukasz Majewski #include <malloc.h>
241b6ca18bSPantelis Antoniou #include <errno.h>
25*ea2453d5SPantelis Antoniou #include <div64.h>
26cb383cd2SLukasz Majewski #include <dfu.h>
27cb383cd2SLukasz Majewski 
28cb383cd2SLukasz Majewski enum dfu_mmc_op {
29cb383cd2SLukasz Majewski 	DFU_OP_READ = 1,
30cb383cd2SLukasz Majewski 	DFU_OP_WRITE,
31cb383cd2SLukasz Majewski };
32cb383cd2SLukasz Majewski 
33*ea2453d5SPantelis Antoniou static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE)
34*ea2453d5SPantelis Antoniou 				dfu_file_buf[CONFIG_SYS_DFU_MAX_FILE_SIZE];
35*ea2453d5SPantelis Antoniou static long dfu_file_buf_len;
36*ea2453d5SPantelis Antoniou 
37cb383cd2SLukasz Majewski static int mmc_block_op(enum dfu_mmc_op op, struct dfu_entity *dfu,
38*ea2453d5SPantelis Antoniou 			u64 offset, void *buf, long *len)
39cb383cd2SLukasz Majewski {
40cb383cd2SLukasz Majewski 	char cmd_buf[DFU_CMD_BUF_SIZE];
41*ea2453d5SPantelis Antoniou 	u32 blk_start, blk_count;
42cb383cd2SLukasz Majewski 
43*ea2453d5SPantelis Antoniou 	/*
44*ea2453d5SPantelis Antoniou 	 * We must ensure that we work in lba_blk_size chunks, so ALIGN
45*ea2453d5SPantelis Antoniou 	 * this value.
46*ea2453d5SPantelis Antoniou 	 */
47*ea2453d5SPantelis Antoniou 	*len = ALIGN(*len, dfu->data.mmc.lba_blk_size);
48*ea2453d5SPantelis Antoniou 
49*ea2453d5SPantelis Antoniou 	blk_start = dfu->data.mmc.lba_start +
50*ea2453d5SPantelis Antoniou 			(u32)lldiv(offset, dfu->data.mmc.lba_blk_size);
51*ea2453d5SPantelis Antoniou 	blk_count = *len / dfu->data.mmc.lba_blk_size;
52*ea2453d5SPantelis Antoniou 	if (blk_start + blk_count >
53*ea2453d5SPantelis Antoniou 			dfu->data.mmc.lba_start + dfu->data.mmc.lba_size) {
54*ea2453d5SPantelis Antoniou 		puts("Request would exceed designated area!\n");
55*ea2453d5SPantelis Antoniou 		return -EINVAL;
56*ea2453d5SPantelis Antoniou 	}
57*ea2453d5SPantelis Antoniou 
58*ea2453d5SPantelis Antoniou 	sprintf(cmd_buf, "mmc %s %p %x %x",
59cb383cd2SLukasz Majewski 		op == DFU_OP_READ ? "read" : "write",
60*ea2453d5SPantelis Antoniou 		 buf, blk_start, blk_count);
61cb383cd2SLukasz Majewski 
62cb383cd2SLukasz Majewski 	debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
63cb383cd2SLukasz Majewski 	return run_command(cmd_buf, 0);
64cb383cd2SLukasz Majewski }
65cb383cd2SLukasz Majewski 
66*ea2453d5SPantelis Antoniou static int mmc_file_buffer(struct dfu_entity *dfu, void *buf, long *len)
67cb383cd2SLukasz Majewski {
68*ea2453d5SPantelis Antoniou 	if (dfu_file_buf_len + *len > CONFIG_SYS_DFU_MAX_FILE_SIZE) {
69*ea2453d5SPantelis Antoniou 		dfu_file_buf_len = 0;
70*ea2453d5SPantelis Antoniou 		return -EINVAL;
71cb383cd2SLukasz Majewski 	}
72cb383cd2SLukasz Majewski 
73*ea2453d5SPantelis Antoniou 	/* Add to the current buffer. */
74*ea2453d5SPantelis Antoniou 	memcpy(dfu_file_buf + dfu_file_buf_len, buf, *len);
75*ea2453d5SPantelis Antoniou 	dfu_file_buf_len += *len;
76*ea2453d5SPantelis Antoniou 
77*ea2453d5SPantelis Antoniou 	return 0;
78cb383cd2SLukasz Majewski }
79cb383cd2SLukasz Majewski 
80cb383cd2SLukasz Majewski static int mmc_file_op(enum dfu_mmc_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:
89*ea2453d5SPantelis 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,
92*ea2453d5SPantelis Antoniou 			(unsigned int) buf, dfu->name);
93*ea2453d5SPantelis Antoniou 		if (op == DFU_OP_WRITE)
94*ea2453d5SPantelis Antoniou 			sprintf(cmd_buf + strlen(cmd_buf), " %lx", *len);
9543e66272SŁukasz Majewski 		break;
9643e66272SŁukasz Majewski 	case DFU_FS_EXT4:
97*ea2453d5SPantelis Antoniou 		sprintf(cmd_buf, "ext4%s mmc %d:%d 0x%x /%s",
9843e66272SŁukasz Majewski 			op == DFU_OP_READ ? "load" : "write",
9943e66272SŁukasz Majewski 			dfu->data.mmc.dev, dfu->data.mmc.part,
100*ea2453d5SPantelis Antoniou 			(unsigned int) buf, dfu->name);
10143e66272SŁukasz Majewski 		break;
10243e66272SŁukasz Majewski 	default:
10343e66272SŁukasz Majewski 		printf("%s: Layout (%s) not (yet) supported!\n", __func__,
10443e66272SŁukasz Majewski 		       dfu_get_layout(dfu->layout));
105*ea2453d5SPantelis Antoniou 		return -1;
10643e66272SŁukasz Majewski 	}
107cb383cd2SLukasz Majewski 
108cb383cd2SLukasz Majewski 	debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
109cb383cd2SLukasz Majewski 
110cb383cd2SLukasz Majewski 	ret = run_command(cmd_buf, 0);
111cb383cd2SLukasz Majewski 	if (ret) {
112cb383cd2SLukasz Majewski 		puts("dfu: Read error!\n");
113cb383cd2SLukasz Majewski 		return ret;
114cb383cd2SLukasz Majewski 	}
115cb383cd2SLukasz Majewski 
11681c1d7b6SŁukasz Majewski 	if (dfu->layout != DFU_RAW_ADDR && op == DFU_OP_READ) {
117cb383cd2SLukasz Majewski 		str_env = getenv("filesize");
118cb383cd2SLukasz Majewski 		if (str_env == NULL) {
119cb383cd2SLukasz Majewski 			puts("dfu: Wrong file size!\n");
120cb383cd2SLukasz Majewski 			return -1;
121cb383cd2SLukasz Majewski 		}
122cb383cd2SLukasz Majewski 		*len = simple_strtoul(str_env, NULL, 16);
123cb383cd2SLukasz Majewski 	}
124cb383cd2SLukasz Majewski 
125cb383cd2SLukasz Majewski 	return ret;
126cb383cd2SLukasz Majewski }
127cb383cd2SLukasz Majewski 
128*ea2453d5SPantelis Antoniou int dfu_write_medium_mmc(struct dfu_entity *dfu,
129*ea2453d5SPantelis Antoniou 		u64 offset, void *buf, long *len)
130cb383cd2SLukasz Majewski {
131cb383cd2SLukasz Majewski 	int ret = -1;
132cb383cd2SLukasz Majewski 
133cb383cd2SLukasz Majewski 	switch (dfu->layout) {
134cb383cd2SLukasz Majewski 	case DFU_RAW_ADDR:
135*ea2453d5SPantelis Antoniou 		ret = mmc_block_op(DFU_OP_WRITE, dfu, offset, buf, len);
136cb383cd2SLukasz Majewski 		break;
137cb383cd2SLukasz Majewski 	case DFU_FS_FAT:
13843e66272SŁukasz Majewski 	case DFU_FS_EXT4:
139*ea2453d5SPantelis Antoniou 		ret = mmc_file_buffer(dfu, buf, len);
140cb383cd2SLukasz Majewski 		break;
141cb383cd2SLukasz Majewski 	default:
142cb383cd2SLukasz Majewski 		printf("%s: Layout (%s) not (yet) supported!\n", __func__,
143cb383cd2SLukasz Majewski 		       dfu_get_layout(dfu->layout));
144cb383cd2SLukasz Majewski 	}
145cb383cd2SLukasz Majewski 
146cb383cd2SLukasz Majewski 	return ret;
147cb383cd2SLukasz Majewski }
148cb383cd2SLukasz Majewski 
149*ea2453d5SPantelis Antoniou int dfu_flush_medium_mmc(struct dfu_entity *dfu)
150*ea2453d5SPantelis Antoniou {
151*ea2453d5SPantelis Antoniou 	int ret = 0;
152*ea2453d5SPantelis Antoniou 
153*ea2453d5SPantelis Antoniou 	if (dfu->layout != DFU_RAW_ADDR) {
154*ea2453d5SPantelis Antoniou 		/* Do stuff here. */
155*ea2453d5SPantelis Antoniou 		ret = mmc_file_op(DFU_OP_WRITE, dfu, &dfu_file_buf,
156*ea2453d5SPantelis Antoniou 				&dfu_file_buf_len);
157*ea2453d5SPantelis Antoniou 
158*ea2453d5SPantelis Antoniou 		/* Now that we're done */
159*ea2453d5SPantelis Antoniou 		dfu_file_buf_len = 0;
160*ea2453d5SPantelis Antoniou 	}
161*ea2453d5SPantelis Antoniou 
162*ea2453d5SPantelis Antoniou 	return ret;
163*ea2453d5SPantelis Antoniou }
164*ea2453d5SPantelis Antoniou 
165*ea2453d5SPantelis Antoniou int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf,
166*ea2453d5SPantelis Antoniou 		long *len)
167cb383cd2SLukasz Majewski {
168cb383cd2SLukasz Majewski 	int ret = -1;
169cb383cd2SLukasz Majewski 
170cb383cd2SLukasz Majewski 	switch (dfu->layout) {
171cb383cd2SLukasz Majewski 	case DFU_RAW_ADDR:
172*ea2453d5SPantelis Antoniou 		ret = mmc_block_op(DFU_OP_READ, dfu, offset, buf, len);
173cb383cd2SLukasz Majewski 		break;
174cb383cd2SLukasz Majewski 	case DFU_FS_FAT:
17543e66272SŁukasz Majewski 	case DFU_FS_EXT4:
176*ea2453d5SPantelis Antoniou 		ret = mmc_file_op(DFU_OP_READ, dfu, buf, len);
177cb383cd2SLukasz Majewski 		break;
178cb383cd2SLukasz Majewski 	default:
179cb383cd2SLukasz Majewski 		printf("%s: Layout (%s) not (yet) supported!\n", __func__,
180cb383cd2SLukasz Majewski 		       dfu_get_layout(dfu->layout));
181cb383cd2SLukasz Majewski 	}
182cb383cd2SLukasz Majewski 
183cb383cd2SLukasz Majewski 	return ret;
184cb383cd2SLukasz Majewski }
185cb383cd2SLukasz Majewski 
186cb383cd2SLukasz Majewski int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s)
187cb383cd2SLukasz Majewski {
1881b6ca18bSPantelis Antoniou 	int dev, part;
1891b6ca18bSPantelis Antoniou 	struct mmc *mmc;
1901b6ca18bSPantelis Antoniou 	block_dev_desc_t *blk_dev;
1911b6ca18bSPantelis Antoniou 	disk_partition_t partinfo;
192cb383cd2SLukasz Majewski 	char *st;
193cb383cd2SLukasz Majewski 
194cb383cd2SLukasz Majewski 	dfu->dev_type = DFU_DEV_MMC;
195cb383cd2SLukasz Majewski 	st = strsep(&s, " ");
196cb383cd2SLukasz Majewski 	if (!strcmp(st, "mmc")) {
197cb383cd2SLukasz Majewski 		dfu->layout = DFU_RAW_ADDR;
198cb383cd2SLukasz Majewski 		dfu->data.mmc.lba_start = simple_strtoul(s, &s, 16);
199cb383cd2SLukasz Majewski 		dfu->data.mmc.lba_size = simple_strtoul(++s, &s, 16);
200cb383cd2SLukasz Majewski 		dfu->data.mmc.lba_blk_size = get_mmc_blk_size(dfu->dev_num);
201cb383cd2SLukasz Majewski 	} else if (!strcmp(st, "fat")) {
202cb383cd2SLukasz Majewski 		dfu->layout = DFU_FS_FAT;
20343e66272SŁukasz Majewski 	} else if (!strcmp(st, "ext4")) {
20443e66272SŁukasz Majewski 		dfu->layout = DFU_FS_EXT4;
2051b6ca18bSPantelis Antoniou 	} else if (!strcmp(st, "part")) {
2061b6ca18bSPantelis Antoniou 
2071b6ca18bSPantelis Antoniou 		dfu->layout = DFU_RAW_ADDR;
2081b6ca18bSPantelis Antoniou 
2091b6ca18bSPantelis Antoniou 		dev = simple_strtoul(s, &s, 10);
2101b6ca18bSPantelis Antoniou 		s++;
2111b6ca18bSPantelis Antoniou 		part = simple_strtoul(s, &s, 10);
2121b6ca18bSPantelis Antoniou 
2131b6ca18bSPantelis Antoniou 		mmc = find_mmc_device(dev);
2141b6ca18bSPantelis Antoniou 		if (mmc == NULL || mmc_init(mmc)) {
215*ea2453d5SPantelis Antoniou 			printf("%s: could not find mmc device #%d!\n",
216*ea2453d5SPantelis Antoniou 			       __func__, dev);
2171b6ca18bSPantelis Antoniou 			return -ENODEV;
2181b6ca18bSPantelis Antoniou 		}
2191b6ca18bSPantelis Antoniou 
2201b6ca18bSPantelis Antoniou 		blk_dev = &mmc->block_dev;
2211b6ca18bSPantelis Antoniou 		if (get_partition_info(blk_dev, part, &partinfo) != 0) {
2221b6ca18bSPantelis Antoniou 			printf("%s: could not find partition #%d on mmc device #%d!\n",
2231b6ca18bSPantelis Antoniou 			       __func__, part, dev);
2241b6ca18bSPantelis Antoniou 			return -ENODEV;
2251b6ca18bSPantelis Antoniou 		}
2261b6ca18bSPantelis Antoniou 
2271b6ca18bSPantelis Antoniou 		dfu->data.mmc.lba_start = partinfo.start;
2281b6ca18bSPantelis Antoniou 		dfu->data.mmc.lba_size = partinfo.size;
2291b6ca18bSPantelis Antoniou 		dfu->data.mmc.lba_blk_size = partinfo.blksz;
2301b6ca18bSPantelis Antoniou 
231cb383cd2SLukasz Majewski 	} else {
232cb383cd2SLukasz Majewski 		printf("%s: Memory layout (%s) not supported!\n", __func__, st);
2331b6ca18bSPantelis Antoniou 		return -ENODEV;
234cb383cd2SLukasz Majewski 	}
235cb383cd2SLukasz Majewski 
23643e66272SŁukasz Majewski 	if (dfu->layout == DFU_FS_EXT4 || dfu->layout == DFU_FS_FAT) {
23743e66272SŁukasz Majewski 		dfu->data.mmc.dev = simple_strtoul(s, &s, 10);
23843e66272SŁukasz Majewski 		dfu->data.mmc.part = simple_strtoul(++s, &s, 10);
23943e66272SŁukasz Majewski 	}
24043e66272SŁukasz Majewski 
241cb383cd2SLukasz Majewski 	dfu->read_medium = dfu_read_medium_mmc;
242cb383cd2SLukasz Majewski 	dfu->write_medium = dfu_write_medium_mmc;
243*ea2453d5SPantelis Antoniou 	dfu->flush_medium = dfu_flush_medium_mmc;
244*ea2453d5SPantelis Antoniou 
245*ea2453d5SPantelis Antoniou 	/* initial state */
246*ea2453d5SPantelis Antoniou 	dfu->inited = 0;
247cb383cd2SLukasz Majewski 
248cb383cd2SLukasz Majewski 	return 0;
249cb383cd2SLukasz Majewski }
250